SpringSecurity整合JWT

SpringSecurity微服务学习(尚硅谷)

介绍

学习springsecurity的代码
地址:https://gitee.com/XuLiZhao/springsecurity.git

项目模块

  • common模块:用来存放项目中的公共资源
    • 子模块service_base:项目中的一些公共类,如异常处理类,工具类等等。
    • 子模块spring_security:项目的安全认证模块,配置项目的安全框架
  • infrastructure模块:项目的基础结构,包含网关等配置
    • 子模块api_gateway:配置项目的网关
  • service模块:用户服务的相关配置

使用说明

  • 项目中需要使用到nacos和redis
  • 这个项目主要是配合学习SpringSecurity来学习的,所以我主要分析SpringSecurity+JWT认证相关过程。
  • 用户登录的请求地址在认证过滤器中指定了,访问即可

SpringSecurity+JWT

依赖

<!-- Spring Security依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
<!--JWT依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
<!--hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.3</version>
        </dependency>

工具类

密码处理:DefaultPasswordEncoder

@Component
public class DefaultPasswordEncoder implements PasswordEncoder {

    public DefaultPasswordEncoder() {
        this(-1);
    }

    public DefaultPasswordEncoder(int strength) {

    }

    //进行MD5加密
    @Override
    public String encode(CharSequence charSequence) {
        return SecureUtil.md5(charSequence.toString());
    }

    //进行密码的比对
    @Override
    public boolean matches(CharSequence charSequence, String encodePassword) {
        return encodePassword.equals(SecureUtil.md5(charSequence.toString()));
    }
}

JWT工具类类:TokenManager

@Component
public class TokenManager {

    /**
     *设置token有效时长
     */
    private final long tokenExpiration = 24*60*60*1000;

    /**
     *编码密钥
     */
    private String tokenSignKey = "123456";

    /**
     *使用JWT根据用户名生成token
     */
    public String createToken(String username){
        String token = Jwts.builder().setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .signWith(SignatureAlgorithm.HS512,tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
        return token;
    }

    /**
     *根据token字符串得到用户信息
     */
    public String getUserInfoFromToken(String token){
        String userInfo = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
        return userInfo;
    }

    /**
     *删除token
     */
    public void removeToken(String token){

    }

}

退出处理器:TokenLogoutHandler

public class TokenLogoutHandler implements LogoutHandler {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        //从header中获取token
        String token = request.getHeader("token");
        //token不为空,移除token,从redis中删除
        if (ObjectUtil.isNotNull(token)){
            //移除
            tokenManager.removeToken(token);

            //从token中获取用户名
            String username = tokenManager.getUserInfoFromToken(token);
            redisTemplate.delete(username);
        }
        ResponseUtil.out(response, R.ok());
    }
}

未授权处理类:UnauthEntryPoint

public class UnauthEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        //返回未授权的相关信息
        ResponseUtil.out(httpServletResponse, R.error());
    }
}

过滤器

认证过滤器:TokenLoginFilter

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;
    private AuthenticationManager authenticationManager;

    public TokenLoginFilter(TokenManager tokenManager, RedisTemplate redisTemplate, AuthenticationManager authenticationManager) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
        this.authenticationManager = authenticationManager;
        this.setPostOnly(false);
        //设置登录路径
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
    }

    /**
     *获取表单提交用户名和密码
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        //获取表单提交的对象
        try {
            User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword(),
                    new ArrayList<>()));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }

    }

    /**
     *认证成功调用的方法
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //认证成功,得到认真成功之后用户信息
        SecurityUser user = (SecurityUser)authResult.getPrincipal();

        //根据用户名生成token
        String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
        //把用户名和用户权限列表放到redis中去
         redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(),user.getPermissionValueList());

        ResponseUtil.out(response, R.ok().data("token",token));
    }

    /**
     *认证失败需要调用的方法
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtil.out(response, R.error());
    }
}

授权过滤器:TokenAuthFilter

public class TokenAuthFilter extends BasicAuthenticationFilter {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenAuthFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
        super(authenticationManager);
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    public TokenAuthFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //获取当前认证成功的用户权限信息
        UsernamePasswordAuthenticationToken authRequest = getAuthention(request);
        //判断如果有权限授权信息,就放到权限上下文中
        if (ObjectUtil.isNotNull(authRequest)){
            SecurityContextHolder.getContext().setAuthentication(authRequest);
        }
        chain.doFilter(request,response);
    }

    public UsernamePasswordAuthenticationToken getAuthention(HttpServletRequest request){
        //从header中获取token
        String token = request.getHeader("token");
        if (ObjectUtil.isNotNull(token)){
            String username = tokenManager.getUserInfoFromToken(token);

            //从redis中获取权限列表信息
            List<String> permissionsValueList = (List<String>) redisTemplate.opsForValue().get(username);
            Collection<GrantedAuthority> authority =  new ArrayList<>();
            for (String permissionsValue : permissionsValueList) {
                SimpleGrantedAuthority auth = new SimpleGrantedAuthority(permissionsValue);
                authority.add(auth);
            }
            return new UsernamePasswordAuthenticationToken(username,token,authority);
        }
        return null;
    }
}

核心配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;
    private DefaultPasswordEncoder defaultPasswordEncoder;
    private UserDetailsService userDetailsService;

    @Autowired
    public TokenWebSecurityConfig(TokenManager tokenManager, RedisTemplate redisTemplate, DefaultPasswordEncoder defaultPasswordEncoder, UserDetailsService userDetailsService) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
        this.defaultPasswordEncoder = defaultPasswordEncoder;
        this.userDetailsService = userDetailsService;
    }

    /**
     * 配置设置
     */
    //设置退出的地址和 token,redis 操作地址
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(new UnauthEntryPoint()) //无权向访问
                .and().csrf().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and().logout().logoutUrl("/admin/acl/index/logout") //设置退出路径
                .addLogoutHandler(new
                        TokenLogoutHandler(tokenManager, redisTemplate)).and() //执行退出的处理器逻辑
                .addFilter(new TokenLoginFilter(tokenManager, redisTemplate, authenticationManager())) //添加认证过滤器
                .addFilter(new
                        TokenAuthFilter(authenticationManager(), tokenManager,
                        redisTemplate)).httpBasic(); //添加授权过滤器
    }

    /**
     *调用userDetailService和密码处理
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception
    {

        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
    }

    /**
     *不进行认证的路径,可以直接访问
     */
    @Override
    public void configure(WebSecurity web) throws Exception{
        web.ignoring().antMatchers("/api/**");
    }

}

UserDetailsService实现类

实现用户认证逻辑

@Service("userDetailsService")
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Autowired
    private PermissionService permissionService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询数据库
        User user = userService.selectByUsername(username);
        //判断
        if (ObjectUtil.isNull(user)){
            throw new UsernameNotFoundException("用户不存在");
        }
        com.atguigu.security.entity.User curUser = new com.atguigu.security.entity.User();
        BeanUtils.copyProperties(user,curUser);
        //根据用户查询出用户列表信息
        List<String> permissionValueList = permissionService.selectPermissionValueByUserId(user.getId());
        SecurityUser securityUser = new SecurityUser();
        securityUser.setPermissionValueList(permissionValueList);
        return securityUser;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值