手记

springboot + springsecurity + jwt认证

  • jwt

    • 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!}
    • 简洁(Compact)
      可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。

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

    • 简介

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

    • 特点

    • java平台的使用

  • spring-security + jwt 实现思路

    • 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

    • 将 JwtAuthenticationTokenFilter 添加到 FilterChain 中

    • 提供一个jwt认证服务:提供 jwt token 的 生成和更新 功能
      (其实就是一个controller)

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

    • 客户端请求认证流程

    • 具体实现

      @Componentpublic 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);
          }
      }
      @EnableWebSecuritypublic 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();
              }
          }
      }
      @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 认证 就已经完成了。

参考文档

Spring Security Reference

jjwt

使用JWT和Spring Security保护REST API



作者:野比大雄1024
链接:https://www.jianshu.com/p/65f8a949dada
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。


0人推荐
随时随地看视频
慕课网APP