SpringBoot Security认证 Redis缓存用户信息【SpringBoot系列10】
SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。
程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发
1 项目准备
- 创建SpringBoot基础项目
- SpringBoot项目集成mybatis
- SpringBoot 集成 Druid 数据源【SpringBoot系列3】
- SpringBoot MyBatis 实现分页查询数据【SpringBoot系列4】
- SpringBoot MyBatis-Plus 集成 【SpringBoot系列5】
- SpringBoot mybatis-plus-generator 代码生成器 【SpringBoot系列6】
- SpringBoot MyBatis-Plus 分页查询 【SpringBoot系列7】
- #SpringBoot 集成Redis缓存 以及实现基本的数据缓存【SpringBoot系列8】
- SpringBoot 整合 Spring Security 实现安全认证【SpringBoot系列9】
在用户的 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