文档章节

Spring Security 整合JWT(四)

郑清
 郑清
发布于 10/23 16:08
字数 1137
阅读 17
收藏 0

一、前言

本篇文章将讲述Spring Security 简单整合JWT 处理认证授权

基本环境
  1. spring-boot 2.1.8
  2. mybatis-plus 2.2.0
  3. mysql 数据库
  4. maven项目
Spring Security入门学习可参考之前文章:
  1. SpringBoot集成Spring Security入门体验(一) https://blog.csdn.net/qq_38225558/article/details/101754743
  2. Spring Security 自定义登录认证(二) https://blog.csdn.net/qq_38225558/article/details/102542072
  3. Spring Security 动态url权限控制(三) https://blog.csdn.net/qq_38225558/article/details/102637637

二、 Spring Security 简单整合 JWT

有关JWT不了解的可以看下官网文档:https://jwt.io/introduction/ 在这里插入图片描述

1、引入jwt依赖

 <!-- jwt依赖: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId>
     <version>0.9.1</version>
 </dependency>

2、在Security登录认证成功后生成jwt令牌返回给前端保存

jwt生成令牌代码如下:

// 生成jwt访问令牌
String jwtToken = Jwts.builder()
        // 用户角色
        .claim("ROLE_LOGIN", "ADMIN")
        // 主题 - 存用户名
        .setSubject("张三")
        // 过期时间 - 30分钟
        .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
        // 加密算法和密钥
        .signWith(SignatureAlgorithm.HS512, "helloworld")
        .compact();

这里贴出小编文末案例demo源码中关于登录认证处理中的使用

@Component
public class AdminAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    UserDetailsServiceImpl userDetailsService;
    @Autowired
    private UserMapper userMapper;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取前端表单中输入后返回的用户名、密码
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();

        SecurityUser userInfo = (SecurityUser) userDetailsService.loadUserByUsername(userName);

        boolean isValid = PasswordUtils.isValidPassword(password, userInfo.getPassword(), userInfo.getCurrentUserInfo().getSalt());
        // 验证密码
        if (!isValid) {
            throw new BadCredentialsException("密码错误!");
        }

        // 前后端分离情况下 处理逻辑...
        // 更新登录令牌
        // 当前用户所拥有角色代码
        String roleCodes = userInfo.getRoleCodes();
        // 生成jwt访问令牌
        String jwt = Jwts.builder()
                // 用户角色
                .claim(Constants.ROLE_LOGIN, roleCodes)
                // 主题 - 存用户名
                .setSubject(authentication.getName())
                // 过期时间 - 30分钟
                .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
                // 加密算法和密钥
                .signWith(SignatureAlgorithm.HS512, Constants.SALT)
                .compact();


        User user = userMapper.selectById(userInfo.getCurrentUserInfo().getId());
        user.setToken(jwt);
        userMapper.updateById(user);
        userInfo.getCurrentUserInfo().setToken(jwt);
        return new UsernamePasswordAuthenticationToken(userInfo, password, userInfo.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

前端页面保存的jwt令牌格式如下: 在这里插入图片描述

3、Security访问鉴权中认证用户信息

我们在访问每一个url请求的时候,在统一认证的地方获取jwt中我们需要的信息然后认证即可,【注: Claims 中存放着我们需要的信息】 例如: 我们可以将用户名、密码存放jwt中,然后在认证的时候读取到其中的用户信息,然后查询数据库认证用户,如果满足条件即成功访问,如果不满足条件即抛出异常处理

温馨小提示:如果jwt令牌过期,会抛出ExpiredJwtException异常,我们需要拦截到,然后交给认证失败处理器中处理,然后返回给前端,这里根据个人业务实际处理即可~

// 获取jwt中的信息
Claims claims = Jwts.parser().setSigningKey("helloworld").parseClaimsJws(jwtToken.replace("Bearer", "")).getBody();
// 获取当前登录用户名
System.out.println("获取当前登录用户名: " + claims.getSubject());

小编项目中认证过滤器中的使用如下:

@Slf4j
@Component
public class MyAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    AdminAuthenticationEntryPoint authenticationEntryPoint;

    private final UserDetailsServiceImpl userDetailsService;

    protected MyAuthenticationFilter(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
        MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(response);
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            // 前后端分离情况下,前端登录后将token储存在cookie中,每次访问接口时通过token去拿用户权限
            String jwtToken = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
            log.debug("后台检查令牌:{}", jwtToken);
            if (StringUtils.isNotBlank(jwtToken)) {
                // JWT相关start ===========================================
                // 获取jwt中的信息
                Claims claims = Jwts.parser().setSigningKey(Constants.SALT).parseClaimsJws(jwtToken.replace("Bearer", "")).getBody();
                // 获取当前登录用户名
                System.out.println("获取当前登录用户名: " + claims.getSubject());
                // TODO 如需使用jwt特性在此做处理~
                // JWT相关end ===========================================

                // 检查token
                SecurityUser securityUser = userDetailsService.getUserByToken(jwtToken);
                if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
                    throw new BadCredentialsException("TOKEN已过期,请重新登录!");
                }
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
                // 全局注入角色权限信息和登录用户基本信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } catch (ExpiredJwtException e) {
            // jwt令牌过期
            SecurityContextHolder.clearContext();
            this.authenticationEntryPoint.commence(wrappedRequest, response, null);
        } catch (AuthenticationException e) {
            SecurityContextHolder.clearContext();
            this.authenticationEntryPoint.commence(wrappedRequest, response, e);
        } finally {
            stopWatch.stop();
        }
    }

}

简单的入门使用就是这样了

三、总结

  1. 引入jwt依赖
  2. 登录系统成功后生成jwt令牌返回给前端保存到浏览器请求头
  3. 在每一次请求访问系统url时,在统一认证过滤器中获取到请求头中jwt令牌中保存的用户信息然后做认证处理,如果满足条件成功访问,如果不满足交给认证失败处理器返回指定内容给前端

本文案例demo源码

https://gitee.com/zhengqingya/java-workspace

© 著作权归作者所有

郑清
粉丝 2
博文 24
码字总数 30539
作品 0
成都
私信 提问
AgileBPM/ab-security-jwt

ab-security-jwt 项目介绍 通过用户分配角色,角色资源授权,配置的形式实现系统鉴权控制(非编码鉴权形式)。 实现了XSS注入检查 防止CSRF攻击 提供获取当前登录用户相关信息工具类 非jwt版...

AgileBPM
2018/08/02
0
0
Spring Security 实战干货: 登录后返回 JWT Token

前言 欢迎阅读 Spring Security 实战干货 系列文章,上一文 我们实现了 JWT 工具。本篇我们将一起探讨如何将 JWT 与 Spring Security 结合起来,在认证成功后不再跳转到指定页面而是直接返回...

码农小胖哥
10/28
1K
3
Spring Security 实战干货:手把手教你实现JWT Token

前言 Json Web Token () 近几年是前后端分离常用的 Token 技术,是目前最流行的跨域身份验证解决方案。你可以通过文章 一文了解web无状态会话token技术JWT 来了解 。今天我们来手写一个通用...

码农小胖哥
10/27
228
0
【spring cloud】自定义jwt实现spring cloud nosession

JWT实现在网关模块,网关的路由是默认配置。jwt 生成、验证依赖 最核心的配置是在spring security中加入我们token校验机制的fiter:JwtAuthenticationTokenFilter 在看我们的spring security...

冷冷gg
2017/08/22
4.3K
9
springboot集成JWT,必须要使用spring security组件吗?

看到网上很多springboot集成JWT的demo,都使用了spring security组件, 想问下大牛们,spring security组件是JWT集成所必须的吗? 我只想单纯的springboot集成JWT,各位有例子参考不? 谢谢了...

jelly_oy
2018/10/11
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

前端的一些雕虫小技,从100%和滚动条说起

1、100%和滚动条 当我们在css中把html和body同时设为100%时,会出现滚动条 html, body { width: 100%; height: 100%; } 原因是html和b...

wphmoon
今天
6
0
电力区块链应用案例【2019】

随着区块链技术的日益普及,出现了大量创业企业尝试使用区块链技术来解决能源与电力行业中存在的问题。在本文中,我们将介绍其中的三个能源区块链项目。 能源行业以价格不透明著称:消费者很...

汇智网教程
今天
9
0
聊聊rocketmq的adjustThreadPoolNumsThreshold

序 本文主要研究一下rocketmq的adjustThreadPoolNumsThreshold DefaultMQPushConsumer rocketmq-client-4.5.2-sources.jar!/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.ja......

go4it
今天
10
0
关于早起

早起是非常好的事情,但是像如果前一天睡得晚,或者第二天早上是非常冷的时候,那就不是很美好了。 但是本身早起是一件非常棒的事情,我记得我每次早起 如果不觉得困的话,世界是那么安静,脑...

T型人才追梦者
今天
7
0
Java输入输出

JDK中的InputStream/OutputStream构成了IO输入输出继承层次的基础。它们都是面向字节序列的,每次可以从序列中读入或者写出一个字节或者指定大小的字节数组。但是面向字节流的输入输出不便于...

ytuan996
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部