天天看點

Spring Boot使用JWT實作系統登入驗證

什麼是JWT(Json Web Token)

jwt是為了在網絡應用環境間傳遞聲明而執行的一種基于json的開放标準。該token被設計緊湊且安全的,特别适用于SSO場景。

jwt的聲明一般被用來在身份提供者和服務提供者之間傳遞被認證的使用者身份資訊。

JWT長什麼樣

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0MDAyIiwiZXhwIjoxNTEwOTcwMjU4fQ._FOqy5l44hODu3DjXh762LNUTLNQH15fdCUerdseDpmSKgkVSCjOyxQNTBKDSh3N-c83_pdEw5t6BdorgRU_kw

JWT的構成

JWT通常由三部分組成,頭資訊(header)、消息體(body)、簽名(signature)

頭資訊指定了JWT使用的簽名算法

header={alg=HS512}

消息體包含了JWT的意圖,exp為令牌過期時間

body={sub=testUsername, exp=1510886546}

簽名通過私鑰生成

signature=kwq8a_B6WMqHOrEi-gFR5rRPmPL7qoShZJn0VFfXpXc1Yfw6BfVrliAP9C4UnXlqD3wRXO3mw_DDIdglN5lH9Q

使用springboot內建jwt

jwt官網

springboot官網

引用依賴

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
  </dependency>

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-actuator</artifactId>
  </dependency>

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
  </dependency>

  <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.7.0</version>
  </dependency>           

建構普通rest接口

@RestController
  @RequestMapping("/employee")
  public class EmployeeController {

      @GetMapping("/greeting")
       public String greeting() {
           return "Hello,World!";
       }
   }           

JwtLoginFilter

public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

   private AuthenticationManager authenticationManager;

   public JwtLoginFilter(AuthenticationManager authenticationManager) {
       this.authenticationManager = authenticationManager;
   }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {
      Employee employee = new Employee();
       return authenticationManager.authenticate(
               new UsernamePasswordAuthenticationToken(
                   employee.getUsername(),
                   employee.getPassword(),
                   new ArrayList<>()
              )
       );
   }

  @Override
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, 
   FilterChain chain, Authentication authResult) throws IOException, ServletException {
      String token = Jwts.builder()
           .setSubject(((User) authResult.getPrincipal()).getUsername())
           .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
           .signWith(SignatureAlgorithm.HS512, "JWTSecret")
           .compact();

       response.addHeader("Authorization", JwtUtils.getTokenHeader(token));
  }           
}

JwtAuthenticationFilter

public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

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

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
      String header = request.getHeader("Authorization");

      if (header == null || !header.startsWith(JwtUtils.getAuthorizationHeaderPrefix())) {
          chain.doFilter(request, response);
         return;
      }

      UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(header);

      SecurityContextHolder.getContext().setAuthentication(authenticationToken);
      chain.doFilter(request, response);
  }

  private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String token) {
      String user = Jwts.parser()
              .setSigningKey("PrivateSecret")
              .parseClaimsJws(token.replace(JwtUtils.getAuthorizationHeaderPrefix(), ""))
              .getBody()
              .getSubject();

      if (null != user) {
          return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
      }

      return null;
  }
  }           

SecurityConfiguration

@Configuration
  @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
  public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Override
    public void configure(WebSecurity web) throws Exception {
          super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http.cors().and().csrf().disable().authorizeRequests()
              .anyRequest().authenticated()
              .and()
              .addFilter(new JwtLoginFilter(authenticationManager()))
              .addFilter(new JwtAuthenticationFilter(authenticationManager()));
    }

  }           

使用postman測試

首先我們先測試/employee/greeting 響應如下:

{

"timestamp": 1510887634904,

"status": 403,

"error": "Forbidden",

"message": "Access Denied",

"path": "/employee/greeting"

很明顯,狀态碼為403,此刻我們如果先登入拿到token後再測試呢,測試如下

Spring Boot使用JWT實作系統登入驗證

登入成功後,我們可以看到headers中已經帶有jwt

authorization →Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0VXNlcm5hbWUiLCJleHAiOjE1MTA4ODkxMDd9.FtdEM0p84ff5CzDcoiQhtm1MF_NfDH2Ij1jspxlTQhuCISIzYdoU40OsFoxam9F1EXeVw2GZdQmArVwMk6HO1A

由于postman在一般情況下不支援自定義header 這個時候我們需要下載下傳一個插件開啟interceptor 開啟後将authorization 放入header繼續測試:

Spring Boot使用JWT實作系統登入驗證
Spring Boot使用JWT實作系統登入驗證

這時我們發現已經成功傳回hello,world!

最後附上代碼GitHub位址:源碼下載下傳

本文轉載自

原文作者:胡運凡

原文連結:https://www.cnblogs.com/huyunfan/p/7850680.html