最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

3.SpringSecurity+登录功能+jwt校验过滤器+redis配置

来源:博客园


(资料图片)

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

@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {    @Autowired    private UserMapper userMapper;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        //根据用户名查询用户信息        LambdaQueryWrapper 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配置类

@Configurationpublic 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.登录校验过滤器

@Componentpublic 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.登录校验过滤器加入到过滤器组中

@Configurationpublic 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 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 <= 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;@Configurationpublic class RedisConfig {    @Bean    @SuppressWarnings(value = { "unchecked", "rawtypes" })    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory)    {        RedisTemplate 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;    }}

关键词: 重新登录 身份验证 没有问题