SpringBoot整合Shiro实现权限控制的代码实现
1、SpringBoot整合Shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
1.1、shiro简介
shiro有个核心组件,分别为Subject、SecurityManager和Realms
- Subject:相当于当前操作的”用户“,这个用户不一定是一个具体的人,是一个抽象的概念,表明的是和当前程序进行交互的任何东西,例如爬虫、脚本、等等。所有的Subject都绑定到SecurityManager上,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者。
- SecurityManager:这个是shiro框架的核心,所有与安全相关的操作都会与它进行交互,它管理者所有的Subject。
- Realms:充当了Shiro与应用安全数据间的”桥梁“,当对用户执行认证(登录)和授权(访问控制)验证时,SecurityManager 需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作。
1.2、代码的具体实现
1.2.1、Maven的配置
org.apache.shiro shiro-spring-boot-starter1.7.1 com.github.theborakompanioni thymeleaf-extras-shiro2.0.0 org.apache.shiro shiro-ehcache1.7.1
shiro默认是与jsp进行使用的,而这里是shiro整合thymeleaf所有要导入shiro整合thymeleaf的jar包
1.2.2、整合需要实现的类
- 一般来说整合只需要完成两个类的实现即可
- 一个是 ShiroConfig 一个是 CustomerRealm
- 如果需要添加shiro缓存并且不是自带的缓存而是redis缓存还需要进行另外两个类的编写
- 一个是 RedisCache 一个是 RedisCacheManager
文章图片
1.2.4、ShiroConfig的实现
未加shiro的缓存
package com.yuwen.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import com.yuwen.shiro.cache.RedisCacheManager; import com.yuwen.shiro.realm.CustomerRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configurationpublic class ShiroConfig {//让页面shiro标签生效@Beanpublic ShiroDialect shiroDialect(){return new ShiroDialect(); }//1、创建shiroFilter负责拦截所有请求@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); //给filter设置安全管理factoryBean.setSecurityManager(defaultWebSecurityManager); //配置系统的受限资源//配置系统公共资源 全部都能访问的设置anonMap map = new HashMap<>(); map.put("/main","authc"); //请求这个资源需要认证和授权 authc表示需要认证后才能访问map.put("/admin","roles[admin]"); //表示admin角色才能访问 roles[]表示需要什么角色才能访问map.put("/manage","perms[user:*:*]"); //表示需要user:*:*权限才能访问 perms[]表示需要什么权限才能访问//访问需要认证的页面如果未登录会跳转到/login路由进行登陆factoryBean.setLoginUrl("/login"); //访问未授权页面会自动跳转到/unAuth路由factoryBean.setUnauthorizedUrl("/unAuth"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; }//2、创建安全管理器@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("getRealm") Realm realm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //给安全管理器设置securityManager.setRealm(realm); return securityManager; }//3、创建自定义的realm@Beanpublic Realm getRealm(){CustomerRealm customerRealm = new CustomerRealm(); //修改凭证校验匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //设置加密算法为md5credentialsMatcher.setHashAlgorithmName("MD5"); //设置散列次数credentialsMatcher.setHashIterations(1024); customerRealm.setCredentialsMatcher(credentialsMatcher); return customerRealm; }}
因为一般在数据库中设置明文密码不安全,所有我这里对密码进行了md5加密,我的加密方式为:密码 = 密码+盐+散列次数 而后进行MD5加密 所以这里创建自定义的realm时需要进行设置匹配器这样登录时密码才能匹配成功
1.2.5、CustomerRealm的实现
package com.yuwen.shiro.realm; import com.yuwen.pojo.User; import com.yuwen.pojo.vo.ViewPerms; import com.yuwen.pojo.vo.ViewRole; import com.yuwen.service.UserService; import com.yuwen.shiro.salt.MyByteSource; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.CollectionUtils; import org.springframework.util.ObjectUtils; import javax.annotation.Resource; import java.util.List; //自定义realmpublic class CustomerRealm extends AuthorizingRealm {@Resourceprivate UserService userService; //授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//获取身份信息String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal(); //根据主身份信息获取角色 和 权限信息Listroles = userService.findRolesByUsername(primaryPrincipal); if (!CollectionUtils.isEmpty(roles)){SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); roles.forEach(viewRole -> {simpleAuthorizationInfo.addRole(viewRole.getName()); //权限信息List perms = userService.findPermsByRoleId(viewRole.getName()); if (!CollectionUtils.isEmpty(perms)){perms.forEach(viewPerms -> {simpleAuthorizationInfo.addStringPermission(viewPerms.getPName()); }); }}); return simpleAuthorizationInfo; }return null; } //认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//获取登入的身份信息String principal = (String) authenticationToken.getPrincipal(); User user = userService.findByUsername(principal); if (!ObjectUtils.isEmpty(user)){//ByteSource.Util.bytes(user.getSalt()) 通过这个工具将盐传入//如果身份认证验证成功,返回一个AuthenticationInfo实现;return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),new MyByteSource(user.getSalt()),this.getName()); }return null; }}
在登录时会自动调用这个身份验证 在验证时如果出错,会报异常,我在controller层接收了异常并处理
controller层中登录时的异常处理
@PostMapping("/login")public String login(String username,String password){//获取主体对象Subject subject = SecurityUtils.getSubject(); try {//自动调用CustomerRealm 类中的身份验证方法subject.login(new UsernamePasswordToken(username,password)); return "index"; }catch (UnknownAccountException e){ //接收异常并处理e.printStackTrace(); model.addAttribute("msg","用户名有误,请重新登录"); }catch (IncorrectCredentialsException e){//接收异常并处理e.printStackTrace(); model.addAttribute("msg","密码有误,请重新登录"); }return "login"; }
1.2.6、shiro缓存配置
定义了shiro缓存,用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率
默认缓存的配置
在 ShiroConfig中 的 getRealm() 方法中开启缓存管理
@Beanpublic Realm getRealm(){CustomerRealm customerRealm = new CustomerRealm(); //开启缓存管理customerRealm.setCacheManager(new EhCacheManager()); //开启全局缓存customerRealm.setCachingEnabled(true); //开启认证缓存customerRealm.setAuthenticationCachingEnabled(true); customerRealm.setAuthenticationCacheName("authenticationCache"); //开启权限缓存customerRealm.setAuthorizationCachingEnabled(true); customerRealm.setAuthorizationCacheName("authorizationCache"); return customerRealm; }
与reids整合的缓存这里就不说明了,放在源码里自己查看,源码在下方
1.2.7、主页index.html的设置
在这里用标签来判断某些区域需要认证或什么角色或者什么权限才能访问
首页 - 锐客网 index退出main | manage | admin用户:显示认证通过内容没有认证时 显示admin角色 显示具有用户模块的"user:*:*"权限 显示
1.3、简单测试
【SpringBoot整合Shiro实现权限控制的代码实现】
文章图片
1.3.1、admin角色所有权限测试
文章图片
1.3.2、无角色有权限测试
文章图片
1.3.3、无角色无权限测试
文章图片
文章图片
1.4 项目源码
需要源码的在这:https://gitee.com/residual-temperature/shiro-demo
到此这篇关于SpringBoot整合Shiro实现权限控制的方法的文章就介绍到这了,更多相关SpringBoot整合Shiro权限控制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- Spring|Spring Boot 整合 Activiti6.0.0
- springboot使用redis缓存
- springboot整合数据库连接池-->druid
- SpringBoot中YAML语法及几个注意点说明
- springboot结合redis实现搜索栏热搜功能及文字过滤
- springboot中.yml文件的值无法读取的问题及解决
- SpringBoot整合MongoDB完整实例代码