你知道的越多,不知道的就越多,业余的像一棵小草!
你来,我们一起精进!你不来,我和你的竞争对手一起精进!
编辑:业余草
推荐:/?p=5059
一.说明
Shiro是一个安全框架,项目中主要用它做认证,授权,加密,以及用户的会话管理,虽然Shiro没有SpringSecurity功能更丰富,但是它轻量,简单,在项目中通常业务需求Shiro也都能胜任.二.项目环境
MyBatis-Plus版本: 3.1.0
SpringBoot版本:2.1.5
JDK版本:1.8
Shiro版本:1.4
Shiro-redis插件版本:3.1.0
数据表(SQL文件在项目中):数据库中测试号的密码进行了加密,密码皆为123456
Maven依赖如下:<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--AOP依赖,一定要加,否则权限拦截验证不生效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!--mybatisPlus核心库-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!--引入阿里数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!--Shiro核心依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--Shiro-redis插件-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.1.0</version>
</dependency>
<!--StringUitlS工具-->
<dependency>
<groupId>mons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
配置如下:#配置端口
server:
port:8764
spring:
#配置数据源
datasource:
driver-class-name:com.mysql.cj.jdbc.Driver
url:jdbc:mysql://localhost:3306/my_shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
username:root
password:root
type:com.alibaba.druid.pool.DruidDataSource
#Redis数据源
redis:
host:localhost
port:6379
timeout:6000
password:123456
jedis:
pool:
max-active:1000#连接池最大连接数(使用负值表示没有限制)
max-wait:-1#连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle:10#连接池中的最大空闲连接
min-idle:5#连接池中的最小空闲连接
#mybatis-plus相关配置
mybatis-plus:
#xml扫描,多个目录用逗号或者分号分隔(告诉Mapper所对应的XML文件位置)
mapper-locations:classpath:mapper/*.xml
#以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型AUTO:"数据库ID自增"INPUT:"用户输入ID",ID_WORKER:"全局唯一ID(数字类型唯一ID)",UUID:"全局唯一IDUUID";
id-type:auto
#字段策略IGNORED:"忽略判断"NOT_NULL:"非NULL判断")NOT_EMPTY:"非空判断"
field-strategy:NOT_EMPTY
#数据库类型
db-type:MYSQL
configuration:
#是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case:true
#如果查询结果中包含空值的列,则MyBatis在映射的时候,不会映射这个字段
call-setters-on-nulls:true
#这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl:org.apache.ibatis.logging.stdout.StdOutImpl
二.编写项目基础类
用户实体,Dao,Service等在这里省略,请参考源码编写Exception类来处理Shiro权限拦截异常
创建SHA256Util加密工具
创建Spring工具/**
*@DescriptionSpring上下文工具类
*@AuthorSans
*@CreateTime/6/1713:40
*/
@Component
publicclassSpringUtilimplementsApplicationContextAware{
privatestaticApplicationContextcontext;
/**
*Spring在bean初始化后会判断是不是ApplicationContextAware的子类
*如果该类是,setApplicationContext()方法,会将容器中ApplicationContext作为参数传入进去
*@AuthorSans
*@CreateTime/6/1716:58
*/
@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
context=applicationContext;
}
/**
*通过Name返回指定的Bean
*@AuthorSans
*@CreateTime/6/1716:03
*/
publicstatic<T>TgetBean(Class<T>beanClass){
returncontext.getBean(beanClass);
}
}
创建Shiro工具/**
*@DescriptionShiro工具类
*@AuthorSans
*@CreateTime/6/1516:11
*/
publicclassShiroUtils{
/**私有构造器**/
privateShiroUtils(){}
privatestaticRedisSessionDAOredisSessionDAO=SpringUtil.getBean(RedisSessionDAO.class);
/**
*获取当前用户Session
*@AuthorSans
*@CreateTime/6/1717:03
*@ReturnSysUserEntity用户信息
*/
publicstaticSessiongetSession(){
returnSecurityUtils.getSubject().getSession();
}
/**
*用户登出
*@AuthorSans
*@CreateTime/6/1717:23
*/
publicstaticvoidlogout(){
SecurityUtils.getSubject().logout();
}
/**
*获取当前用户信息
*@AuthorSans
*@CreateTime/6/1717:03
*@ReturnSysUserEntity用户信息
*/
publicstaticSysUserEntitygetUserInfo(){
return(SysUserEntity)SecurityUtils.getSubject().getPrincipal();
}
/**
*删除用户缓存信息
*@AuthorSans
*@CreateTime/6/1713:57
*@Paramusername用户名称
*@ParamisRemoveSession是否删除Session
*@Returnvoid
*/
publicstaticvoiddeleteCache(Stringusername,booleanisRemoveSession){
//从缓存中获取Session
Sessionsession=null;
Collection<Session>sessions=redisSessionDAO.getActiveSessions();
SysUserEntitysysUserEntity;
Objectattribute=null;
for(SessionsessionInfo:sessions){
//遍历Session,找到该用户名称对应的Session
attribute=sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if(attribute==null){
continue;
}
sysUserEntity=(SysUserEntity)((SimplePrincipalCollection)attribute).getPrimaryPrincipal();
if(sysUserEntity==null){
continue;
}
if(Objects.equals(sysUserEntity.getUsername(),username)){
session=sessionInfo;
}
}
if(session==null||attribute==null){
return;
}
//删除session
if(isRemoveSession){
redisSessionDAO.delete(session);
}
//删除Cache,在访问受限接口时会重新授权
DefaultWebSecurityManagersecurityManager=(DefaultWebSecurityManager)SecurityUtils.getSecurityManager();
Authenticatorauthc=securityManager.getAuthenticator();
((LogoutAware)authc).onLogout((SimplePrincipalCollection)attribute);
}
}
创建Shiro的SessionId生成器
三.编写Shiro核心类
创建Realm用于授权和认证/**
*@DescriptionShiro权限匹配和账号密码匹配
*@AuthorSans
*@CreateTime/6/1511:27
*/
publicclassShiroRealmextendsAuthorizingRealm{
@Autowired
privateSysUserServicesysUserService;
@Autowired
privateSysRoleServicesysRoleService;
@Autowired
privateSysMenuServicesysMenuService;
/**
*授权权限
*用户进行权限验证时候Shiro会去缓存中找,如果查不到数据,会执行这个方法去查权限,并放入缓存中
*@AuthorSans
*@CreateTime/6/1211:44
*/
@Override
protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipalCollection){
SimpleAuthorizationInfoauthorizationInfo=newSimpleAuthorizationInfo();
SysUserEntitysysUserEntity=(SysUserEntity)principalCollection.getPrimaryPrincipal();
//获取用户ID
LonguserId=sysUserEntity.getUserId();
//这里可以进行授权和处理
Set<String>rolesSet=newHashSet<>();
Set<String>permsSet=newHashSet<>();
//查询角色和权限(这里根据业务自行查询)
List<SysRoleEntity>sysRoleEntityList=sysRoleService.selectSysRoleByUserId(userId);
for(SysRoleEntitysysRoleEntity:sysRoleEntityList){
rolesSet.add(sysRoleEntity.getRoleName());
List<SysMenuEntity>sysMenuEntityList=sysMenuService.selectSysMenuByRoleId(sysRoleEntity.getRoleId());
for(SysMenuEntitysysMenuEntity:sysMenuEntityList){
permsSet.add(sysMenuEntity.getPerms());
}
}
//将查到的权限和角色分别传入authorizationInfo中
authorizationInfo.setStringPermissions(permsSet);
authorizationInfo.setRoles(rolesSet);
returnauthorizationInfo;
}
/**
*身份认证
*@AuthorSans
*@CreateTime/6/1212:36
*/
@Override
protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokenauthenticationToken)throwsAuthenticationException{
//获取用户的输入的账号.
Stringusername=(String)authenticationToken.getPrincipal();
//通过username从数据库中查找User对象,如果找到进行验证
//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
SysUserEntityuser=sysUserService.selectUserByName(username);
//判断账号是否存在
if(user==null){
thrownewAuthenticationException();
}
//判断账号是否被冻结
if(user.getState()==null||user.getState().equals("PROHIBIT")){
thrownewLockedAccountException();
}
//进行验证
SimpleAuthenticationInfoauthenticationInfo=newSimpleAuthenticationInfo(
user,//用户名
user.getPassword(),//密码
ByteSource.Util.bytes(user.getSalt()),//设置盐值
getName()
);
//验证成功开始踢人(清除缓存和Session)
ShiroUtils.deleteCache(username,true);
returnauthenticationInfo;
}
}
创建SessionManager类
创建ShiroConfig配置类/**
*@DescriptionShiro配置类
*@AuthorSans
*@CreateTime/6/1017:42
*/
@Configuration
publicclassShiroConfig{
privatefinalStringCACHE_KEY="shiro:cache:";
privatefinalStringSESSION_KEY="shiro:session:";
privatefinalintEXPIRE=1800;
//Redis配置
@Value("${spring.redis.host}")
privateStringhost;
@Value("${spring.redis.port}")
privateintport;
@Value("${spring.redis.timeout}")
privateinttimeout;
@Value("${spring.redis.password}")
privateStringpassword;
/**
*开启Shiro-aop注解支持
*@Attention使用代理方式所以需要开启代码支持
*@AuthorSans
*@CreateTime/6/128:38
*/
@Bean
publicAuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(SecurityManagersecurityManager){
AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor=newAuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
returnauthorizationAttributeSourceAdvisor;
}
/**
*Shiro基础配置
*@AuthorSans
*@CreateTime/6/128:42
*/
@Bean
publicShiroFilterFactoryBeanshiroFilterFactory(SecurityManagersecurityManager){
ShiroFilterFactoryBeanshiroFilterFactoryBean=newShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String>filterChainDefinitionMap=newLinkedHashMap<>();
//注意过滤器配置顺序不能颠倒
//配置过滤:不会被拦截的链接
filterChainDefinitionMap.put("/static/**","anon");
filterChainDefinitionMap.put("/userLogin/**","anon");
filterChainDefinitionMap.put("/**","authc");
//配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
shiroFilterFactoryBean.setLoginUrl("/userLogin/unauth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
returnshiroFilterFactoryBean;
}
/**
*安全管理器
*@AuthorSans
*@CreateTime/6/1210:34
*/
@Bean
publicSecurityManagersecurityManager(){
DefaultWebSecurityManagersecurityManager=newDefaultWebSecurityManager();
//自定义Ssession管理
securityManager.setSessionManager(sessionManager());
//自定义Cache实现
securityManager.setCacheManager(cacheManager());
//自定义Realm验证
securityManager.setRealm(shiroRealm());
returnsecurityManager;
}
/**
*身份验证器
*@AuthorSans
*@CreateTime/6/1210:37
*/
@Bean
publicShiroRealmshiroRealm(){
ShiroRealmshiroRealm=newShiroRealm();
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
returnshiroRealm;
}
/**
*凭证匹配器
*将密码校验交给Shiro的SimpleAuthenticationInfo进行处理,在这里做匹配配置
*@AuthorSans
*@CreateTime/6/1210:48
*/
@Bean
publicHashedCredentialsMatcherhashedCredentialsMatcher(){
HashedCredentialsMatchershaCredentialsMatcher=newHashedCredentialsMatcher();
//散列算法:这里使用SHA256算法;
shaCredentialsMatcher.setHashAlgorithmName(SHA256Util.HASH_ALGORITHM_NAME);
//散列的次数,比如散列两次,相当于md5(md5(""));
shaCredentialsMatcher.setHashIterations(SHA256Util.HASH_ITERATIONS);
returnshaCredentialsMatcher;
}
/**
*配置Redis管理器
*@Attention使用的是shiro-redis开源插件
*@AuthorSans
*@CreateTime/6/1211:06
*/
@Bean
publicRedisManagerredisManager(){
RedisManagerredisManager=newRedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setTimeout(timeout);
redisManager.setPassword(password);
returnredisManager;
}
/**
*配置Cache管理器
*用于往Redis存储权限和角色标识
*@Attention使用的是shiro-redis开源插件
*@AuthorSans
*@CreateTime/6/1212:37
*/
@Bean
publicRedisCacheManagercacheManager(){
RedisCacheManagerredisCacheManager=newRedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
redisCacheManager.setKeyPrefix(CACHE_KEY);
//配置缓存的话要求放在session里面的实体类必须有个id标识
redisCacheManager.setPrincipalIdFieldName("userId");
returnredisCacheManager;
}
/**
*SessionID生成器
*@AuthorSans
*@CreateTime/6/1213:12
*/
@Bean
publicShiroSessionIdGeneratorsessionIdGenerator(){
returnnewShiroSessionIdGenerator();
}
/**
*配置RedisSessionDAO
*@Attention使用的是shiro-redis开源插件
*@AuthorSans
*@CreateTime/6/1213:44
*/
@Bean
publicRedisSessionDAOredisSessionDAO(){
RedisSessionDAOredisSessionDAO=newRedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
redisSessionDAO.setKeyPrefix(SESSION_KEY);
redisSessionDAO.setExpire(expire);
returnredisSessionDAO;
}
/**
*配置Session管理器
*@AuthorSans
*@CreateTime/6/1214:25
*/
@Bean
publicSessionManagersessionManager(){
ShiroSessionManagershiroSessionManager=newShiroSessionManager();
shiroSessionManager.setSessionDAO(redisSessionDAO());
returnshiroSessionManager;
}
}
四.实现权限控制
Shiro可以用代码或者注解来控制权限,通常我们使用注解控制,不仅简单方便,而且更加灵活.Shiro注解一共有五个:
一般情况下我们在项目中做权限控制,使用最多的是RequiresPermissions和RequiresRoles,允许存在多个角色和权限,默认逻辑是AND,也就是同时拥有这些才可以访问方法,可以在注解中以参数的形式设置成OR示例
使用顺序:Shiro注解是存在顺序的,当多个注解在一个方法上的时候,会逐个检查,知道全部通过为止,默认拦截顺序是:RequiresRoles->RequiresPermissions->RequiresAuthentication->
RequiresUser->RequiresGuest示例
创建UserRoleController角色拦截测试类/**
*@Description角色测试
*@AuthorSans
*@CreateTime/6/1911:38
*/
@RestController
@RequestMapping("/role")
publicclassUserRoleController{
@Autowired
privateSysUserServicesysUserService;
@Autowired
privateSysRoleServicesysRoleService;
@Autowired
privateSysMenuServicesysMenuService;
@Autowired
privateSysRoleMenuServicesysRoleMenuService;
/**
*管理员角色测试接口
*@AuthorSans
*@CreateTime/6/1910:38
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getAdminInfo")
@RequiresRoles("ADMIN")
publicMap<String,Object>getAdminInfo(){
Map<String,Object>map=newHashMap<>();
map.put("code",200);
map.put("msg","这里是只有管理员角色能访问的接口");
returnmap;
}
/**
*用户角色测试接口
*@AuthorSans
*@CreateTime/6/1910:38
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getUserInfo")
@RequiresRoles("USER")
publicMap<String,Object>getUserInfo(){
Map<String,Object>map=newHashMap<>();
map.put("code",200);
map.put("msg","这里是只有用户角色能访问的接口");
returnmap;
}
/**
*角色测试接口
*@AuthorSans
*@CreateTime/6/1910:38
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getRoleInfo")
@RequiresRoles(value={"ADMIN","USER"},logical=Logical.OR)
@RequiresUser
publicMap<String,Object>getRoleInfo(){
Map<String,Object>map=newHashMap<>();
map.put("code",200);
map.put("msg","这里是只要有ADMIN或者USER角色能访问的接口");
returnmap;
}
/**
*登出(测试登出)
*@AuthorSans
*@CreateTime/6/1910:38
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getLogout")
@RequiresUser
publicMap<String,Object>getLogout(){
ShiroUtils.logout();
Map<String,Object>map=newHashMap<>();
map.put("code",200);
map.put("msg","登出");
returnmap;
}
}
创建UserMenuController权限拦截测试类/**
*@Description权限测试
*@AuthorSans
*@CreateTime/6/1911:38
*/
@RestController
@RequestMapping("/menu")
publicclassUserMenuController{
@Autowired
privateSysUserServicesysUserService;
@Autowired
privateSysRoleServicesysRoleService;
@Autowired
privateSysMenuServicesysMenuService;
@Autowired
privateSysRoleMenuServicesysRoleMenuService;
/**
*获取用户信息集合
*@AuthorSans
*@CreateTime/6/1910:36
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getUserInfoList")
@RequiresPermissions("sys:user:info")
publicMap<String,Object>getUserInfoList(){
Map<String,Object>map=newHashMap<>();
List<SysUserEntity>sysUserEntityList=sysUserService.list();
map.put("sysUserEntityList",sysUserEntityList);
returnmap;
}
/**
*获取角色信息集合
*@AuthorSans
*@CreateTime/6/1910:37
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getRoleInfoList")
@RequiresPermissions("sys:role:info")
publicMap<String,Object>getRoleInfoList(){
Map<String,Object>map=newHashMap<>();
List<SysRoleEntity>sysRoleEntityList=sysRoleService.list();
map.put("sysRoleEntityList",sysRoleEntityList);
returnmap;
}
/**
*获取权限信息集合
*@AuthorSans
*@CreateTime/6/1910:38
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getMenuInfoList")
@RequiresPermissions("sys:menu:info")
publicMap<String,Object>getMenuInfoList(){
Map<String,Object>map=newHashMap<>();
List<SysMenuEntity>sysMenuEntityList=sysMenuService.list();
map.put("sysMenuEntityList",sysMenuEntityList);
returnmap;
}
/**
*获取所有数据
*@AuthorSans
*@CreateTime/6/1910:38
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/getInfoAll")
@RequiresPermissions("sys:info:all")
publicMap<String,Object>getInfoAll(){
Map<String,Object>map=newHashMap<>();
List<SysUserEntity>sysUserEntityList=sysUserService.list();
map.put("sysUserEntityList",sysUserEntityList);
List<SysRoleEntity>sysRoleEntityList=sysRoleService.list();
map.put("sysRoleEntityList",sysRoleEntityList);
List<SysMenuEntity>sysMenuEntityList=sysMenuService.list();
map.put("sysMenuEntityList",sysMenuEntityList);
returnmap;
}
/**
*添加管理员角色权限(测试动态权限更新)
*@AuthorSans
*@CreateTime/6/1910:39
*@Paramusername用户ID
*@ReturnMap<String,Object>返回结果
*/
@RequestMapping("/addMenu")
publicMap<String,Object>addMenu(){
//添加管理员角色权限
SysRoleMenuEntitysysRoleMenuEntity=newSysRoleMenuEntity();
sysRoleMenuEntity.setMenuId(4L);
sysRoleMenuEntity.setRoleId(1L);
sysRoleMenuService.save(sysRoleMenuEntity);
//清除缓存
Stringusername="admin";
ShiroUtils.deleteCache(username,false);
Map<String,Object>map=newHashMap<>();
map.put("code",200);
map.put("msg","权限添加成功");
returnmap;
}
}
创建UserLoginController登录类/**
*@Description用户登录
*@AuthorSans
*@CreateTime/6/1715:21
*/
@RestController
@RequestMapping("/userLogin")
publicclassUserLoginController{
@Autowired
privateSysUserServicesysUserService;
/**
*登录
*@AuthorSans
*@CreateTime/6/209:21
*/
@RequestMapping("/login")
publicMap<String,Object>login(@RequestBodySysUserEntitysysUserEntity){
Map<String,Object>map=newHashMap<>();
//进行身份验证
try{
//验证身份和登陆
Subjectsubject=SecurityUtils.getSubject();
UsernamePasswordTokentoken=newUsernamePasswordToken(sysUserEntity.getUsername(),sysUserEntity.getPassword());
//验证成功进行登录操作
subject.login(token);
}catch(IncorrectCredentialsExceptione){
map.put("code",500);
map.put("msg","用户不存在或者密码错误");
returnmap;
}catch(LockedAccountExceptione){
map.put("code",500);
map.put("msg","登录失败,该用户已被冻结");
returnmap;
}catch(AuthenticationExceptione){
map.put("code",500);
map.put("msg","该用户不存在");
returnmap;
}catch(Exceptione){
map.put("code",500);
map.put("msg","未知异常");
returnmap;
}
map.put("code",0);
map.put("msg","登录成功");
map.put("token",ShiroUtils.getSession().getId().toString());
returnmap;
}
/**
*未登录
*@AuthorSans
*@CreateTime/6/209:22
*/
@RequestMapping("/unauth")
publicMap<String,Object>unauth(){
Map<String,Object>map=newHashMap<>();
map.put("code",500);
map.put("msg","未登录");
returnmap;
}
}
五.POSTMAN测试
登录成功后会返回TOKEN,因为是单点登录,再次登陆的话会返回新的TOKEN,之前Redis的TOKEN就会失效了
当第一次访问接口后我们可以看到缓存中已经有权限数据了,在次访问接口的时候,Shiro会直接去缓存中拿取权限,注意访问接口时候要设置请求头.
ADMIN这个号现在没有sys:info:all这个权限的,所以无法访问getInfoAll接口,我们要动态分配权限后,要清掉缓存,在访问接口时候,Shiro会去重新执行授权方法,之后再次把权限和角色数据放入缓存中
访问添加权限测试接口,因为是测试,我把增加权限的用户ADMIN写死在里面了,权限添加后,调用工具类清掉缓存,我们可以发现,Redis中已经没有缓存了
再次访问getInfoAll接口,因为缓存中没有数据,Shiro会重新授权查询权限,拦截通过
六.项目源码
/liselotte/spring-boot-shiro-demo
/xuyulong/my-java-demo