java过滤器过滤好指定请求(3.SpringSecurity+登录功能+jwt校验过滤器+redis配置)
导读:SpringSecurity+登录功能+jwt校验过滤器+redis配置 一、思路分析...
SpringSecurity+登录功能+jwt校验过滤器+redis配置
一 、思路分析
1.登录
①自定义登录接口 调用ProviderManager的方法进行认证 如果认证通过生成jwt 把用户信息存入redis中 ②自定义UserDetailsService 在这个实现类中去查询数据库 注意配置passwordEncoder为BCryptPasswordEncoder2.校验:
①定义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序列化
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<T> implements RedisSerializer<T> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class<T> clazz; static { ParserConfig.getGlobalInstance().setAutoTypeSupport(true); } public FastJsonRedisSerializer(Class<T> 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 <= 0) { return null; } String str = new String(bytes, DEFAULT_CHARSET); return JSON.parseObject(str, clazz); } protected JavaType getJavaType(Class<?> clazz) { return TypeFactory.defaultInstance().constructType(clazz); } }** RedisConfig Redis配置
package com.lwq.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean @SuppressWarnings(value = { "unchecked", "rawtypes" }) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!