SpringBoot + SpringSecurity + JWT认证

参考文档
Spring Security Reference
jjwt
使用JWT和Spring Security保护REST API

  • jwt

    • 简介

      JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

    • 特点

      • 简洁(Compact)
        可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。

      • 自包含(Self-contained)
        负载中包含了所有用户所需要的信息,避免了多次查询数据库。

    • java平台的使用

      • maven 支持

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        
      • 生成 token

        import io.jsonwebtoken.Jwts;
        import io.jsonwebtoken.SignatureAlgorithm;
        import io.jsonwebtoken.impl.crypto.MacProvider;
        import java.security.Key;
        
        // We need a signing key, so we'll create one just for this example. Usually
        // the key would be read from your application configuration instead.
        Key key = MacProvider.generateKey();
        
        String compactJws = Jwts.builder()
          .setSubject("Joe")
          .signWith(SignatureAlgorithm.HS512, key)
          .compact();
        
      • 解析token

        String compactJws = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.yiV1GWDrQyCeoOswYTf_xvlgsnaVVYJM0mU6rkmRBf2T1MBl3Xh2kZii0Q9BdX5-G0j25Qv2WF4lA6jPl5GKuA";
        try {
            Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);
            //OK, we can trust this JWT
        } catch (SignatureException e) {
            //don't trust the JWT!
        }
        
  • spring-security + jwt 实现思路

    • 在spring-security原本的FilterChain中,
      添加 jwt认证用的Filter :JwtAuthenticationTokenFilter。

    • 客户端请求认证流程

    • 具体实现

      • application.properties 配置

            ##============JSON Web Token========================================
        jwt.header=Authorization
        jwt.secret=mySecret
        jwt.expiration=604800            jwt.route.authentication.path=auth
        jwt.route.authentication.refresh=refresh
        jwt.route.authentication.register="auth/register"
        
      • 添加 JwtAuthenticationTokenFilter

      @Component
      public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
      
          private final Log logger = LogFactory.getLog(this.getClass());
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Autowired
          private JwtTokenUtil jwtTokenUtil;
      
          @Value("${jwt.header}")
          private String tokenHeader;
      
          @Override
          protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
              System.out.println("进来了 JwtAuthenticationTokenFilter");
      
              // 得到 请求头的 认证信息 authToken
              String authToken = request.getHeader(this.tokenHeader);
      
              // 解析 authToken 得到 用户名
              String username = jwtTokenUtil.getUsernameFromToken(authToken);
      
              System.out.println("checking authentication for user " + username);
      
              if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
      
                  // 根据用户名从数据库查找用户信息
                  UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
      
                  // 检验token是否有效,并检验其保存的用户信息是否正确
                  if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                      // token 有效,为该请求装载 用户权限信息
                      UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                      authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                      logger.info("authenticated user " + username + ", setting security context");
                      SecurityContextHolder.getContext().setAuthentication(authentication);
                  }
              }
              System.out.println("出去了 JwtAuthenticationTokenFilter");
              chain.doFilter(request, response);
          }
      }
      
      • 将 JwtAuthenticationTokenFilter 添加到 FilterChain 中
      @EnableWebSecurity
      public class MultiHttpSecurityConfig {
      
          @Configuration
          public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
      
              @Autowired
              private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
      
              // 静态资源访问的 url
              private String[] staticFileUrl = {};
              // 不用认证就可访问的 url
              private String[] permitUrl = {};
      
              @Override
              public void configure(WebSecurity web) throws Exception {
                  web.ignoring().antMatchers(staticFileUrl);
                  web.ignoring().antMatchers(permitUrl);
              }
      
              @Override
              protected void configure(HttpSecurity http) throws Exception {
                  http.csrf().disable();
      
                  // 访问url认证
                  http
                          .authorizeRequests()
                          .antMatchers("/admin/**").hasAuthority(String.valueOf(AuthorityName.ROLE_ADMIN))
                          .anyRequest().authenticated();
                  // 配置登陆信息
                  http
                          .formLogin().loginPage("/login")
                          .defaultSuccessUrl("/goIndex")
                          .permitAll()
                          .and();
                  // 配置退出登陆信息
                  http
                          .logout()
                          .logoutSuccessUrl("/login")
                          .invalidateHttpSession(true)
                          .deleteCookies()
                          .and();
      
                  http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);
      
                  http.httpBasic();
              }
          }
      }
      
      • 提供一个jwt认证服务:提供 jwt token 的 生成和更新 功能
        (其实就是一个controller)
      @RestController
      @RequestMapping("authentication")
      public class AuthenticationRestController {
      
          private final Log logger = LogFactory.getLog(this.getClass());
      
          @Value("${jwt.header}")
          private String tokenHeader;
      
          @Autowired
          private AuthenticationManager authenticationManager;
      
          @Autowired
          private JwtTokenUtil jwtTokenUtil;
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
          public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest, Device device) throws AuthenticationException {
              System.out.println("进来了 createAuthenticationToken ");
      
              System.out.println("authenticationRequest : " + authenticationRequest.getPassword() + "::" + authenticationRequest.getUsername());
      
              // Perform the security
              final Authentication authentication = authenticationManager.authenticate(
                      new UsernamePasswordAuthenticationToken(
                              authenticationRequest.getUsername(),
                              authenticationRequest.getPassword()
                      )
              );
              SecurityContextHolder.getContext().setAuthentication(authentication);
      
              // Reload password post-security so we can generate token
              final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
              final String token = jwtTokenUtil.generateToken(userDetails, device);
      
              // Return the token
              return ResponseEntity.ok(new JwtAuthenticationResponse(token));
          }
      }
      
    • 至此,spring-security 整合 jwt 认证 就已经完成了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值