权限管理03-security登陆后鉴权

1 大坑解决(security登录认证成功后,获取的用户一直是匿名用户)

【问题复现】

登陆成功后,调用其他接口,总是自动进入到我们的匿名用户未授权的过滤器,也就是前文写的这里

【问题分析】 

security在UsernamePasswordAuthenticationFilter过滤器进行登录参数获取,但是之前还有一个过滤器SecurityContextPersistenceFilter,看下源码

源码中这个过滤器会清除掉

SecurityContextHolder中的Context,导致我们SecurityContextHolder.getContext().getAuthentication();肯定会出问题

【问题解决】

思路如下:

既然SecurityContextPersistenceFilter过滤器在UsernamePasswordAuthenticationFilter之前就把SecurityContextHolder中的Context清除了,那我们可以写一个过滤器,重新把SecurityContextHolder的context设置进去,并且此过滤器要放在UsernamePasswordAuthenticationFilter之后,那么就非常容易了

security中的过滤器一般都继承OncePerRequestFilter

package com.grm.security;

import com.alibaba.fastjson.JSON;
import com.grm.common.Result;
import com.grm.security.details.LoginUser;
import com.grm.util.JwtUtil;
import com.grm.util.RedisUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 解决天坑坑坑坑坑----------------
 * 登录成功,一直401,显示匿名用户鉴权失败,是因为SecurityContextHolder自己把Context清楚了,我们需要重新设置一下
 *
 * @author gaorimao
 * @date 2022/02/10
 */
@Slf4j
@Component
public class MyOncePerRequestFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if(!"/login".equals(request.getRequestURI())){
            log.info("[RequestURI] path = {}",request.getRequestURI());
            String token = request.getHeader("Authorization");
            if(ObjectUtils.isEmpty(token)){
                Result.renderJsonStr(response,Result.failed(500,"获取token失败!"));
            }
            Claims claims = jwtUtil.parseToken(token);
            String username = claims.getSubject();
            if(ObjectUtils.isEmpty(username)){
                Result.renderJsonStr(response,Result.failed(500,"token解析失败!"));
            }
            Object redisTokenObj = redisUtil.get("Authorization:"+username);
            if(redisTokenObj == null){
                Result.renderJsonStr(response,Result.failed(500,"token已过期!"));
            }
            Object loginUserObj = redisUtil.get("LoginUser:"+username);
            if(loginUserObj == null){
                Result.renderJsonStr(response,Result.failed(500,"获取当前登录用户失败!"));
            }
            LoginUser loginUser = (LoginUser)loginUserObj;
            log.info("[LoginUser] redis loginUser = {}", JSON.toJSONString(loginUser));

            /*
                重新设置SecurityContextHolder
             */
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, loginUser.getPassword(), loginUser.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        filterChain.doFilter(request, response);
    }
}

把这个过滤器配置在SecurityConfig中

@Autowired
private MyOncePerRequestFilter myOncePerRequestFilter;

// 重新设置SecurityContextHolder
http.addFilterBefore(myOncePerRequestFilter, UsernamePasswordAuthenticationFilter.class);

 至此大坑解决!

2 授权验证

其实至此,我们授权已经写好了,不过还是来验证下

首先我们再来捋一下

manager用户是有审批权限的(oa:approve:list)

user用户是没有审批权限的

 那我们先用user账号登录,拿到user用户的token,再用postman工具发送请求OA审批列表的接口

user用户

 manager用户

至此,基于security的权限控制到按钮,后台所有逻辑基本已经实现完成!

【登出时,又遇到大坑了】

前台调用/logout请求,一直显示跨域问题

之前把跨域配置类注入到security了

实际上不用写 http.addFilterBefor(crosFilter,XXX.class);这句话,删掉即可

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值