ibcadmin 发表于 2019-10-24 09:50:31

Spring Security 整合JWT(四)

<h3 id="一前言">一、前言</h3>
<p>本篇文章将报告<code>Spring Security 简单整合JWT 处理认证授权</code></p>
<h6 id="根本环境">根本环境</h6>
<ol>
<li>spring-boot 2.1.8</li>
<li>mybatis-plus 2.2.0</li>
<li>mysql 数据库</li>
<li>maven项目</li>
</ol>
<h6 id="spring-security入门学习可参考之前文章">Spring Security入门学习可参考之前文章:</h6>
<ol>
<li>SpringBoot集成Spring Security入门体验(一)<br />
https://blog.csdn.net/qq_38225558/article/details/101754743</li>
<li>Spring Security 自定义登录认证(二)<br />
https://blog.csdn.net/qq_38225558/article/details/102542072</li>
<li>Spring Security 动态url权限控制(三)<br />
https://blog.csdn.net/qq_38225558/article/details/102637637</li>
</ol>
<h3 id="二-spring-security-简单整合-jwt">二、 Spring Security 简单整合 JWT</h3>
<p>有关<code>JWT</code>不了解的可以看下官网文档:https://jwt.io/introduction/<br />
<div align="center"></div></p>
<h4 id="引入jwt依赖">1、引入jwt依赖</h4>
<code> <!-- jwt依赖: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
   <groupId>io.jsonwebtoken</groupId>
   <artifactId>jjwt</artifactId>
   <version>0.9.1</version>
</dependency></code>
<h4 id="在security登录认证乐成后生成jwt令牌返回给前端生存">2、在Security登录认证乐成后生成jwt令牌返回给前端生存</h4>
<p>jwt生成令牌代码如下:</p>
<code>// 生成jwt访问令牌
String jwtToken = Jwts.builder()
      // 用户脚色
      .claim("ROLE_LOGIN", "ADMIN")
      // 主题 - 存用户名
      .setSubject("张三")
      // 逾期时间 - 30分钟
      .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
      // 加密算法和密钥
      .signWith(SignatureAlgorithm.HS512, "helloworld")
      .compact();</code>
<p>这里贴出小编文末案例demo源码中关于登录认证处理中的使用</p>
<code>@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;
    }
}</code>
<p>前端页面生存的jwt令牌格式如下:<br />
<div align="center"></div></p>
<h4 id="security访问鉴权中认证用户信息">3、Security访问鉴权中认证用户信息</h4>
<p>我们在访问每一个url请求的时间,在同一认证的地方获取jwt中我们必要的信息然后认证即可,【注: <code>Claims</code> 中存放着我们必要的信息】<br />
比方: 我们可以将用户名、密码存放jwt中,然后在认证的时间读取到此中的用户信息,然后查询数据库认证用户,假如满意条件即乐成访问,假如不满意条件即抛出非常处理</p>
<blockquote>
<p><strong>温馨小提示</strong>:假如jwt令牌逾期,会抛出<code>ExpiredJwtException</code>非常,我们必要拦截到,然后交给认证失败处理器中处理,然后返回给前端,这里根据个人业务实际处理即可~</p>
</blockquote>
<code>// 获取jwt中的信息
Claims claims = Jwts.parser().setSigningKey("helloworld").parseClaimsJws(jwtToken.replace("Bearer", "")).getBody();
// 获取当前登录用户名
System.out.println("获取当前登录用户名: " + claims.getSubject());</code>
<p>小编项目中认证过滤器中的使用如下:</p>
<code>@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();
      }
    }

}</code>
<p>简单的入门使用就是这样了</p>
<h3 id="三总结">三、总结</h3>
<ol>
<li>引入<code>jwt依赖</code></li>
<li>登录体系乐成后<code>生成jwt令牌</code>返回给前端生存到<code>欣赏器请求头</code>中</li>
<li>在每一次请求访问体系url时,在同一认证过滤器中获取到请求头中jwt令牌中生存的<code>用户信息</code>然后做<code>认证处理</code>,假如满意条件乐成访问,假如不满意交给认证失败处理器返回指定内容给前端</li>
</ol>
<h4 id="本文案例demo源码">本文案例demo源码</h4>
<p>https://gitee.com/zhengqingya/java-workspace</p><br><br/><br/><br/><br/><br/>来源:<a href="https://www.cnblogs.com/zhengqing/p/11726855.html" target="_blank">https://www.cnblogs.com/zhengqing/p/11726855.html</a>
页: [1]
查看完整版本: Spring Security 整合JWT(四)