Spring Security实现用户授权

思路:

  1. 进行登录,根据用户名查询用户操作权限,查询当前登录用户操作权限数据放到Redis中(key:username value:权限数据)。
  2. 校验的时候,从请求头获取token字符串,从token中获取username,用username到redis中查询权限数据。

详细思路

  1. 修改配置类,添加redis部分
  2. 修改loadUserByUsername接口方法:根据用户名查询用户操作权限数据,封装返回。
  3. 修改TokenLoginFilter,增加权限数据部分:获取当前登录用户的权限数据,把权限数据放到redis中
  4. 修改TokenAuthenticationFilter:从请求头获取token,从token获取username,从username到redis查询权限数据
  5. 在controller层加上权限判断注解

具体实现

  1. 修改loadUserByUsername接口方法:根据用户名查询用户操作权限数据,封装返回
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserService userService;

    @Autowired
    private SysMenuService menuService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询用户
        SysUser user = userService.getUserByUserName(username); //需要在SysUserService中具体实现功能
        if(null == user) {
            throw new UsernameNotFoundException("用户名不存在!");
        }

        if(user.getStatus().intValue() == 0) {
            throw new RuntimeException("账号已停用");
        }
//##################################################
        //根据用户id查询用户操作权限数据
        List<String> buttonList = menuService.getButtonByUserId(user.getId());
        //把权限数据封装成需要的格式
        List<SimpleGrantedAuthority> authList = new ArrayList<>();
        for (String button : buttonList) {
            authList.add(new SimpleGrantedAuthority(button.trim()));
        }
        return new CustomUser(user, authList);
    }
//#################################################
}
  1. 修改TokenLoginFilter,增加权限数据部分:获取当前登录用户的权限数据,把权限数据放到redis中
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;

    //构造方法
    public TokenLoginFilter(AuthenticationManager authenticationManager,RedisTemplate redisTemplate) {
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        //指定登录接口及提交方式,可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login","POST"));
        this.redisTemplate = redisTemplate;
    }

    //登录认证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        try {
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());

            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //登录成功
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        CustomUser customUser = (CustomUser) authResult.getPrincipal();
        String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername());
//#####################################
        //把权限数据保存在redis中
        redisTemplate.opsForValue().set(customUser.getUsername(), JSON.toJSON(customUser.getAuthorities()));
//#####################################
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        ResponseUtil.out(response, Result.ok(map));
    }
  1. 修改TokenAuthenticationFilter:从请求头获取token,从token获取username,从username到redis查询权限数据
public class TokenAuthenticationFilter extends OncePerRequestFilter {


    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        //如果是登录接口,直接放行
        if("/admin/system/index/login".equals(httpServletRequest.getRequestURI())) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = getAuthentication(httpServletRequest);
        if(null != authentication) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } else {
            ResponseUtil.out(httpServletResponse, Result.build(null, ResultCodeNum.PERMISSION));
        }

    }


    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        // token置于header里
        String token = request.getHeader("token");
        logger.info("token:"+token);
        if (!StringUtils.isEmpty(token)) {
            String useruame = JwtHelper.getUsername(token);
            logger.info("useruame:"+useruame);
            if (!StringUtils.isEmpty(useruame)) {
//#########################################
                //通过username从redis中获取权限数据
                String authString = (String) redisTemplate.opsForValue().get(useruame);
                //把redis中字符串类型的数据转换为所需的数据类型
                if (!StringUtils.isEmpty(authString)){
                    List<Map> mapList = JSON.parseArray(authString, Map.class);
                    List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                    for (Map map : mapList) {
                        SimpleGrantedAuthority authority = new SimpleGrantedAuthority((String)(map.get("authority")));
                        authorities.add(authority);
                    }
                    return new UsernamePasswordAuthenticationToken(useruame, null, authorities);
                }else {
                    return new UsernamePasswordAuthenticationToken(useruame, null, Collections.emptyList());
                }
            }
        }
        return null;
    }
}
posted @ 2024-04-24 11:56  ︶ㄣ演戲ㄣ  阅读(37)  评论(0)    收藏  举报