Spring Security真正的实时更新权限方法!无需拦截器、踢出用户,直接修改Redis的内容

相信很多用了Spring Security的小伙伴在日常开发中,总会遇到需要实时更新权限的问题,比如,对一个新调入某个部门的员工,我们希望在管理员设置他的权限之后,这位员工立刻就能使用了,而不需要先退出登录然后再重新登录,以达到更好的用户体验。但是Spring Security似乎并没有直接给出如何按照用户名修改Redis中存储的权限的接口,一般都得是用户自己调用一个更新权限的接口才能更新。那么,我们能不能在后台悄悄地就把权限更新,而不需要用户自己访问呢?

目前网络上一搜,关于这个的内容很少,能搜到的也只是简单的处理方法,例如:

  1. 修改权限后强制踢出用户,这个的用户体验并不好;
  2. 修改权限后用Http客户端携带对应SessionID的Cookie伪装成用户请求更新权限接口(或者是前端不定时访问一下更新权限的接口);
  3. 添加拦截器,管理员更新了权限后,将这些用户的用户名或SessionID作为key,要修改的权限作为value放入一个HashMap中,用户每次请求后端都会经过这个拦截器,拦截器中判断是否需要更新权限。

以上2、3都有一个问题,就是更新权限并不是经常发生的事情,可能用户用了几个月,管理员才会修改一次权限,如果只是为了确认权限有没有更新,就多一个步骤,那属实是有点浪费性能。

接下来,我就带大家看看我是怎么操作的。

首先,我们的项目采用的是Spring Security + Spring Session Redis搭建的。

我们建立了一个测试用的Controller,我们首先要注入SessionRepository

@Autowired
private SessionRepository sessionRepository;

SessionRepository是一个Spring Session的接口,下面有许多实现类。

然后,例如我们如果想要根据SessionID获取一个Session,非常简单,只需要:

Session byId = sessionRepository.findById(session.getId());

Session到手,接下来我们想要获取Spring Security存储在其中的SecurityContext、用户名啥的,只需要:

SecurityContextImpl springSecurityContext = byId.getAttribute("SPRING_SECURITY_CONTEXT");
Authentication authentication = springSecurityContext.getAuthentication();
Object principal = authentication.getPrincipal();

那么,这个时候,我们就可以为所欲为了!例如,我编写了如下示例代码来演示具体如何修改

@PutMapping("/add/{cookieId}/{name}")
public String add(@PathVariable String name, @PathVariable String cookieId) {
    Session byId = sessionRepository.findById(cookieId);
    SecurityContextImpl springSecurityContext = byId.getAttribute("SPRING_SECURITY_CONTEXT");
    Authentication authentication = springSecurityContext.getAuthentication();
    // 获取现有权限
    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    // 放入新的List中    
    List<GrantedAuthority> grantedAuthorities = new ArrayList<>(authorities);
    // 获取要添加的权限列表
    List<GrantedAuthority> newAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(name);
    // 合并
    grantedAuthorities.addAll(newAuthorities);
    // 必须构造新的AuthenticationToken,因为Spring Security没有给出setAuthorities的方法
    springSecurityContext.setAuthentication(new UsernamePasswordAuthenticationToken(authentication.getPrincipal(),authentication.getCredentials(), grantedAuthorities));
    // 设置到Session中
    byId.setAttribute("SPRING_SECURITY_CONTEXT", springSecurityContext);
    // 保存修改
    sessionRepository.save(byId);
    return "success";
}

当然啦,实际上管理员修改完权限,还需要把修改后的权限持久化到数据库中,这样的话用户以后再登录也是有新的权限了!

那么,可能会有小伙伴问了,我能不能直接通过RedisTemplate获取对应的值,强转为SecurityContext呢?很遗憾,答案是不可以,在写下这篇文章的前一晚我也是这么想的,结果发现类型转换是不会通过的,会报错,不管是用StringRedisTemplate还是RedisTemplate都没用。

@GetMapping("/getRoleV2/{cookieId}")
public String getRoleV2(@PathVariable String cookieId) {
    Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("spring:session:sessions:" + cookieId);
    System.out.println(entries);
    SecurityContextImpl springSecurityContext = (SecurityContextImpl) entries.get("sessionAttr:SPRING_SECURITY_CONTEXT");
    Authentication authentication = springSecurityContext.getAuthentication();
    return JSON.toJSONString(springSecurityContext);
}

报错信息:

 java.lang.ClassCastException: class java.lang.String cannot be cast to class org.springframework.security.core.context.SecurityContextImpl (java.lang.String is in module java.base of loader 'bootstrap'; org.springframework.security.core.context.SecurityContextImpl is in unnamed module of loader 'app')
    at com.dayuhelper.usercenter.controller.TestController.getRoleV2(TestController.java:166) 
 
// 以下报错省略

大家赶紧也去试一试吧~
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/qq_37436987/article/details/128887939

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值