使用JWT實現Spring Boot令牌認證

banq發表於2017-02-21
如何可靠實現Rest服務和客戶端之間的使用者身份驗證/授權的方式?

最原始的方式是為每個請求傳送基本的HTTP驗證頭部憑證資訊(使用者名稱/密碼),但這需要將這些憑證儲存在記憶體中,服務必須每次檢查這些憑證(口令雜湊操作是很費CPU的昂貴操作)。所以這不是最好的主意。

還有一種方式,REST服務使用令牌系統實現安全驗證。標準令牌系統在成功登入時返回“令牌”(只是一個長的唯一字串的隨機字元,例如GUID)。客戶端然後在每次請求的HTTP授權報頭中傳送此令牌。在處理請求的每個服務中,然後在後端伺服器中查詢上下文分解分析該令牌。上下文可以儲存在DB中,也可從Redis快取中檢索,或簡單地儲存在記憶體的雜湊表中。這種方法的缺點是,對於每個REST方法,您都將需要在資料庫或快取中執行查詢。

JSON Web Tokens(或簡稱JWT)也是一種令牌,它不僅是使用者唯一的令牌,而且還包含該使用者所需的任何資訊,即所謂的宣告。最基本的宣告是“Subject主題”(基本上是唯一的使用者ID),但是令牌可以擴充套件為包括任何你想要的資訊,可以是api訪問許可權或使用者角色; 您可以在使用者登入時簡單地向使用者新增“角色Role”陣列,其中包含“使用者”許可權和“管理”許可權。只要客戶端將JWT傳送到伺服器,就可以從JWT檢索這些宣告。

顯然,這個令牌是純文字,新增設定很方便;JWT使用安全金鑰加密(僅伺服器已知)或簽名。JWT使用的最常見的方法是透過簽名方式使用。這個JWT的“簽名”位稱為JWS,JSON Web Signature。當您希望客戶端能夠讀取的令牌中資訊時,可以使用此方法。base-64編碼用於簽名但不會加密。

另一種方法是使用JWE,JSON Web Encryption。使用JSON Web加密,您可以使用行業標準加密方法來加密令牌的內容。只有伺服器可以建立和解密令牌,因此這意味著客戶端無法讀取或更改內容,因為它不知道怎麼加密的。

讓我們來看一些實際的程式碼。開源小例子專案按這裡,使用spring boot實現JWT的案例。

首先,我們從登陸開始。它由/ user / login路由處理:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@RequestBody User login) throws ServletException {

    String jwtToken = "";

    if (login.getEmail() == null || login.getPassword() == null) {
        throw new ServletException("Please fill in username and password");
    }

    String email = login.getEmail();
    String password = login.getPassword();

    User user = userService.findByEmail(email);

    if (user == null) {
        throw new ServletException("User email not found.");
    }

    String pwd = user.getPassword();

    if (!password.equals(pwd)) {
        throw new ServletException("Invalid login. Please check your name and password.");
    }

    jwtToken = Jwts.builder().setSubject(email).claim("roles", "user").setIssuedAt(new Date())
            .signWith(SignatureAlgorithm.HS256, "secretkey").compact();

    return jwtToken;
}

<p class="indent">


當使用者使用正確的密碼登入時,他會收到以下令牌:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtQGFib3VsbGFpdGUubWUiLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE0ODYyMDYwNjd9.nbppPf6DIl3f3d79EGouJ1cN599R0JELjAiGHXUqSD0


這個“令牌”之後會用於客戶端對伺服器的後續API呼叫系列請求。這裡的標準方法是傳送具有“Bearer”令牌的授權報頭。HTTP頭部將是:

authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtQGFib3VsbGFpdGUubWUiLCJyb2xlcyI6InVzZXIiLCJpYXQiOjE0ODYyMDU3MTh9.N3quHsQvaqzpCLIPhm7-5_gvmK9TxVrKygCEiis27h


SpringBootJwtApplication會配置一個過濾器。Servlet過濾器可以使用HttpRequests做各種事情,我們將使用這個過濾器來保護我們的“安全”端點。你可以看到SpringBootJwtApplication類將我們的JwtFilter配置為只對“/ secure / *”端點執行操作:

    final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new JwtFilter());
    registrationBean.addUrlPatterns("/secure/*");

    return registrationBean;
<p class="indent">

2
這樣,當我們呼叫/user/login沒有授權頭時,它不會出錯。過濾器負責檢查正確的授權頭是否存在以及Http頭部中的令牌是否有效:

public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
        throws IOException, ServletException {

    final HttpServletRequest request = (HttpServletRequest) req;
    final HttpServletResponse response = (HttpServletResponse) res;
    final String authHeader = request.getHeader("authorization");

    if ("OPTIONS".equals(request.getMethod())) {
        response.setStatus(HttpServletResponse.SC_OK);

        chain.doFilter(req, res);
    } else {

        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new ServletException("Missing or invalid Authorization header");
        }

        final String token = authHeader.substring(7);

        try {
            final Claims claims = Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody();
            request.setAttribute("claims", claims);
        } catch (final SignatureException e) {
            throw new ServletException("Invalid token");
        }

        chain.doFilter(req, res);
    }
}
<p class="indent">


Jwt解析器使用與簽名相同的金鑰來檢查令牌簽名。如果金鑰有效,我們就在請求物件中儲存包含一些使用者資訊(電子郵件,角色)的“Claims”,以便API端點可以使用它。

最後但並非最不重要的是,我們呼叫chain.doFilter這樣,沒有它,請求不會傳遞到我們的控制器。

現在我們有了過濾器,我們可以定義一些漂亮的超級安全的API方法,比如:

@RequestMapping("/user/users")
public String loginSuccess() {
    return "Login Successful!";
}
<p class="indent">

您可以使用像Postman這樣的REST客戶端演示這個例子。



Spring Boot token authentication using JWT

相關文章