SpringSecurity+登录功能+jwt校验过滤器+redis配置
一、思路分析
1.登录
- ①自定义登录接口
- 调用ProviderManager的方法进行认证 如果认证通过生成jwt
- 把用户信息存入redis中
- ②自定义UserDetailsService
- 在这个实现类中去查询数据库
- 注意配置passwordEncoder为BCryptPasswordEncoder
复制代码 2.校验:
- ①定义Jwt认证过滤器
- 获取token
- 解析token获取其中的userid
- 从redis中获取用户信息
- 存入SecurityContextHolder
复制代码 二、登录接口代码实现(第一次登陆获取jwt)
1.业务代码
- @Autowired
- private AuthenticationManager authenticationManager;
- @Autowired
- private RedisCache redisCache;
- @Override
- public ResponseResult login(User user) {
- //1,使用springsecurity功能认证,把用户名密码存入令牌
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
- //2.1,默认使用UserDetailService去内存中找user用户,需要定义Impl实现类来重写查询方法,改成从数据库查询
- //2.2,UserDetailServiceImpl从数据库查询出user返回到authenticate这里。具体查看a类
- Authentication authenticate = authenticationManager.authenticate(authenticationToken);
- //2.3,判断是否认证通过
- if(Objects.isNull(authenticate)){
- throw new RuntimeException("用户名或密码错误");
- }
- //3.1,获取userid 生成token
- LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
- String userId = loginUser.getUser().getId().toString();
- //3.2,生成jwt
- String jwt = JwtUtil.createJWT(userId);
- //3.3,把用户信息存入redis
- redisCache.setCacheObject("bloglogin:"+userId,loginUser);
- //4.1,把token和userinfo封装 返回
- //4.2,把User转换成UserInfoVo
- UserInfoVo userInfoVo = BeanCopyUtils.copyBean(loginUser.getUser(), UserInfoVo.class);
- BlogUserLoginVo vo = new BlogUserLoginVo(jwt,userInfoVo);
- return ResponseResult.okResult(vo);
- }
复制代码 2.a类:UserDetailsServiceImpl
- @Service
- public class UserDetailsServiceImpl implements UserDetailsService {
- @Autowired
- private UserMapper userMapper;
- @Override
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- //根据用户名查询用户信息
- LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(User::getUserName,username);
- User user = userMapper.selectOne(queryWrapper);
- //判断是否查到用户 如果没查到抛出异常
- if(Objects.isNull(user)){
- throw new RuntimeException("用户不存在");
- }
- //返回用户信息
- // TODO 查询权限信息封装
- return new LoginUser(user);
- }
- }
复制代码 3.SecurityConfig配置类
- @Configuration
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- @Bean
- public PasswordEncoder passwordEncoder(){
- return new BCryptPasswordEncoder();
- }
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- //关闭csrf
- .csrf().disable()
- //不通过Session获取SecurityContext
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and()
- .authorizeRequests()
- // 对于登录接口 允许匿名访问
- .antMatchers("/login").anonymous()
- // 除上面外的所有请求全部不需要认证即可访问
- .anyRequest().permitAll();
- http.logout().disable();
- //允许跨域
- http.cors();
- }
- @Override
- @Bean
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
- }
复制代码 三、登录校验过滤器代码实现(校验jwt)
1.登录校验过滤器
- @Component
- public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
- @Autowired
- private RedisCache redisCache;
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, IOException, ServletException {
- //1,获取请求头中的token
- String token = request.getHeader("token");
- if(!StringUtils.hasText(token)){
- //说明该接口不需要登录直接放行,如果是第一次登陆的话跳转到登陆去获取token
- filterChain.doFilter(request, response);
- return;
- }
- //2,解析获取userid
- Claims claims = null;
- try {
- //String jwt = JwtUtil.createJWT(userId);jwt内容为id
- claims = JwtUtil.parseJWT(token);
- } catch (Exception e) {
- e.printStackTrace();
- //token超时 token非法
- //响应告诉前端需要重新登录
- ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
- WebUtils.renderString(response, JSON.toJSONString(result));
- return;
- }
- String userId = claims.getSubject();
- //3,从redis中获取用户信息
- LoginUser loginUser = redisCache.getCacheObject("bloglogin:" + userId);
- //如果获取不到
- if(Objects.isNull(loginUser)){
- //说明登录过期 提示重新登录
- ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
- WebUtils.renderString(response, JSON.toJSONString(result));
- return;
- }
- //4,存入SecurityContextHolder
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,null);
- //UPToken令牌存入Security上下文的设置身份验证属性中,后面过滤器会从Security上下文这里获取用户信息
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
- filterChain.doFilter(request, response);
- }
- }
复制代码 2.登录校验过滤器加入到过滤器组中
- @Configuration
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- @Override
- @Bean
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
- //1,注入登录校验过滤器
- @Autowired
- private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- //关闭csrf
- .csrf().disable()
- //不通过Session获取SecurityContext
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and()
- .authorizeRequests()
- // 对于登录接口 允许匿名访问
- .antMatchers("/login").anonymous()
- //jwt过滤器测试用,如果测试没有问题吧这里删除了
- .antMatchers("/link/getAllLink").authenticated()
- // 除上面外的所有请求全部不需要认证即可访问
- .anyRequest().permitAll();
- http.logout().disable();
- //***2,把jwtAuthenticationTokenFilter添加到SpringSecurity的过滤器链中
- http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
- //允许跨域
- http.cors();
- }
- @Bean
- public PasswordEncoder passwordEncoder(){
- return new BCryptPasswordEncoder();
- }
- }
复制代码 *** Redis使用FastJson序列化
[code]package com.lwq.config;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.serializer.SerializerFeature;import com.fasterxml.jackson.databind.JavaType;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.type.TypeFactory;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.SerializationException;import com.alibaba.fastjson.parser.ParserConfig;import org.springframework.util.Assert;import java.nio.charset.Charset;/** * Redis使用FastJson序列化 * * @author sg */public class FastJsonRedisSerializer implements RedisSerializer{ public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class clazz; static { ParserConfig.getGlobalInstance().setAutoTypeSupport(true); } public FastJsonRedisSerializer(Class clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException { if (t == null) { return new byte[0]; } return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); } @Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length |