SpringBoot Security认证 Redis缓存用户信息【SpringBoot系列10】

在用户的 login 接口中 Spring Security 认证通过后,获取到用户的详情,然后保存到 Redis 中,其中 token 为 Redis 缓存 用户的key .


@Slf4j
@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JWTGenerator jwtGenerator;

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("login")
    public R login(@RequestBody LoginRequest loginDto){
        log.info("登录认证开始 {}",loginDto.toString());
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginDto.getUserName(),
                        loginDto.getPassword()));
        // 认证成功存储认证信息到上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);

        log.info("登录认证完成 {}",loginDto.toString());
        String token = jwtGenerator.generateToken(authentication);
        log.info("登录认证生成 token {}",token);

        String name = authentication.getName();
        //查询用户的详情
        UserInfo userInfo = userService.getByUsername(name);
        //保存用户信息到 redis 中
        ValueOperations<String, UserInfo> operations = redisTemplate.opsForValue();
        //保存用户信息到 redis 中
        operations.set(token, userInfo,7*24*3600, TimeUnit.SECONDS);

        return R.okData(token);
    }
 }
复制代码

然后咱们在 Spring Security 配置的拦截器中,token 校验通过后,从 Redis 中查询缓存的用户信息,将用户信息的 UserId 配置到请求头中

import com.alibaba.fastjson.JSONObject;
import com.biglead.demo.pojo.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
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;
import java.util.HashMap;
import java.util.Map;

public class JWTAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JWTGenerator tokenGenerator;
    @Autowired
    private CustomUserDetailsService customUserDetailsService;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        //获取请求头中的 token 信息
        String token = getJWTFromRequest(request);
        //校验token
        if(StringUtils.hasText(token) && tokenGenerator.validateToken(token)) {
            //解析 token 中的用户信息 (用户的唯一标识 )
            String username = tokenGenerator.getUsernameFromJWT(token);

            UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null,
                    userDetails.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            //获取用户信息
            ValueOperations<String, UserInfo> operations = redisTemplate.opsForValue();
            UserInfo userInfo = operations.get(token);
            if(userInfo==null){
                Map<String,Object> map = new HashMap<>();
                map.put("code",403);
                map.put("message","登录过期,重新登录");
                response.getWriter().print(JSONObject.toJSONString(map));
                return;
            }
            
            //自定义Request 
            HttpServletRequest req = (HttpServletRequest) request;

            XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(req);
            //添加自定义头
            xssRequest.putHeader("userId", userInfo.getUserId().toString());
            xssRequest.putHeader("userName", userInfo.getUserName().toString());

            filterChain.doFilter(xssRequest, response);
            return;
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 就是校验请求头的一种格式 可以随便定义
     * 只要可以解析 就可以
     * @param request
     * @return
     */
    private String getJWTFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7, bearerToken.length());
        }
        return null;
    }
}
复制代码

这里的核心实现 就是 XssHttpServletRequestWrapper

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
  
    private final Map<String, String> customHeaders;

    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        this.customHeaders = new HashMap<String, String>();
    }

    public void putHeader(String name, String value) {
        this.customHeaders.put(name, value);
    }

    public String getHeader(String name) {
        // check the custom headers first
        String headerValue = customHeaders.get(name);

        if (headerValue != null) {
            return headerValue;
        }
        // else return from into the original wrapped object
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    public Enumeration<String> getHeaderNames() {
        // create a set of the custom header names
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        // now add the headers from the wrapped request object
        @SuppressWarnings("unchecked")
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            // add the names of the request headers into the list
            String n = e.nextElement();
            set.add(n);
        }

        // create an enumeration from the set and return
        return Collections.enumeration(set);
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        String values = getHeader(name);
        Set<String> set = Arrays.asList(values.split(",")).stream().collect(Collectors.toSet());
        if (customHeaders.containsKey(name)) {
            return Collections.enumeration(set);
        }
        return super.getHeaders(name);
    }
}
复制代码

这里是自定义的 HttpServletRequestWrapper ,本小节是对 请求Request 中的请求头的操作,后续咱们还会在这里 获取请求体中的参数

然后咱们在请求其他接口的时候 ,就可以通过 @RequestHeader 注解来获取对应的信息,如这里获取 设置的用户信息

@Api(tags="用户分类模块")
@RestController()
@RequestMapping("/auto/userCategory")
@Slf4j
public class UserCategoryController {

    @Resource
    IUserCategoryService userCategoryService;
    @GetMapping(value="/findPage")
    @ApiOperation(value = "分页查询")
    public Object findPage(
            @RequestHeader String userId,
            @RequestHeader String userName,
            @RequestParam(required = false,defaultValue = "1") Integer index,
                           @RequestParam(required = false,defaultValue = "10") Integer pageSize) {
        log.info("请求头 userId {} userName {}",userId,userName);
        Object page = userCategoryService.findPage(index, pageSize);
        return page;
    }
}
复制代码

本文章是系列文章 ,每节文章都有对应的代码,每节的源码都是在上一节的基础上配置而来,对应的视频讲解课程正在火速录制中。 项目源码在这里 :gitee.com/android.lon… 有兴趣可以关注一下公众号:biglead

作者:早起的年轻人
链接:https://juejin.cn/post/7215895579464974392
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值