問題描述
我正在用 Jersey
制作一個 rest API.我正在使用 java-jwt
(https://github.com/auth0/java-jwt) 用于我的令牌生成工作.請檢查以下代碼.
I am making a rest API with Jersey
. I am using java-jwt
(https://github.com/auth0/java-jwt) for my token generation work. Please check the below code.
UserJSONInfo - REST 方法類
UserJSONInfo - REST methods class
@Path ("/user_info")
public class UserInfoJSONService
{
@POST
@Path("/authenticateUser")
@Produces(MediaType.APPLICATION_JSON)
public Response authenticateUser(Credentials credentials)
{
UserInfoService service = new UserInfoService();
try{
UserInfo authenticateUser = service.authenticateUser(credentials.getUserName(), credentials.getPassword());
String generateToken = service.generateToken(AuthKey.authorizationSecret);
Authentication auth = new Authentication();
auth.setIdUser(authenticateUser.getIduser());
auth.setToken(generateToken);
return Response.status(Response.Status.OK).entity(auth).build();
//return authenticateUser;
}
catch(IndexOutOfBoundsException e)
{
throw new WebApplicationException(Response.Status.BAD_REQUEST);
} catch (JWTCreationException ex) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
} catch (UnsupportedEncodingException ex) {
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
}
}
UserInfoService - 服務層
public class UserInfoService {
private static UserInfoDAOInterface userDAOInterface;
public UserInfoService() {
userDAOInterface = new UserInfoDAOImpl();
}
public Session getSession() {
Session session = userDAOInterface.openCurrentSession();
return session;
}
public Transaction getTransaction(Session session) {
Transaction transaction = userDAOInterface.openTransaction(session);
return transaction;
}
public UserInfo authenticateUser(String userName, String password)
{
return authenticate(userName, password);
}
private UserInfo authenticate(String userName, String password) throws IndexOutOfBoundsException
{
Session session = userDAOInterface.openCurrentSession();
Transaction transaction = null;
UserInfo user = new UserInfo();
try {
transaction = userDAOInterface.openTransaction(session);
user = userDAOInterface.authenticate(userName, password, session);
transaction.commit();
// } catch (Exception ex) {
// //ex.printStackTrace();
// System.out.println("OK");
//
} finally {
session.close();
}
return user;
}
public String generateToken(String secret) throws JWTCreationException, UnsupportedEncodingException
{
Token token = new Token();
return token.issueTokenHMAC256(secret);
}
}
AuthKey - 只包含 secret
public interface AuthKey {
public static String authorizationSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>test.ExampleServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>Jersey RESTful Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>rest</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/ExampleServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Jersey RESTful Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
我已將我的令牌生成類作為另一個 java 項目進行維護,并將其作為庫導入此處(我使用的是 Netbeans).下面是代碼
I have maintained my token generation classes as another java project and imported it here as a library (I am using Netbeans). Below is the code
package com.xyz.security;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.io.UnsupportedEncodingException;
/**
*
* @author Yohan
*/
public class Token {
/**
* Generate the HMAC256 Token
* @param secret
* Secret to generate the token
* @return
* Token as a String
* @throws UnsupportedEncodingException
* UTF-8 encoding not supported
* @throws JWTVerificationException
* Invalid Signing configuration / Couldn't convert Claims.
*/
public String issueTokenHMAC256(String secret) throws UnsupportedEncodingException, JWTCreationException
{
String token="";
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
token = JWT.create()
.withIssuer("auth0")
.sign(algorithm);
} catch (UnsupportedEncodingException exception) {
//UTF-8 encoding not supported
exception.printStackTrace();
} catch (JWTCreationException exception) {
//Invalid Signing configuration / Couldn't convert Claims.
exception.printStackTrace();
}
return token;
}
/**
* Validate a HMAC256 Token
* @param token
* Token you need to validate
* @param secret
* Secret used to generate the token
* @return
* Returns `true` if token is valid.
* @throws UnsupportedEncodingException
* UTF-8 encoding not supported
* @throws JWTVerificationException
* Invalid Signing configuration / Couldn't convert Claims.
*/
public boolean validateTokenHMAC256(String token, String secret) throws UnsupportedEncodingException, JWTVerificationException
{
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
return true;
}
}
現在的問題是,每次我在用戶登錄時生成 token
時,都會得到相同的 token
.我正在使用 POSTMAN
來檢查 REST 方法,我打開了 3 個選項卡并嘗試登錄 3 個不同的用戶.問題是我得到了相同的令牌!這是正確的還是錯誤的?在這種情況下,我該如何解決?
Now the problem is, everytime I generate the token
when a user login, I am getting the same token
. I am using POSTMAN
to check REST methods, I opened 3 tabs and made a login attempt for 3 different users. The issue is I am getting the same token! Is this is correct or something wrong? In that case how can I fix it?
推薦答案
什么是基于令牌的認證方案
在基于令牌的身份驗證方案中,令牌成為用戶的憑證.用戶名和密碼等硬憑證被交換為必須在每個請求中發送的令牌,然后服務器可以執行身份驗證/授權.令牌可以在短時間內有效,可以撤銷,可以攜帶范圍詳細信息(可以使用令牌請求的內容)等.
What authentication scheme based on tokens is about
In an authentication scheme based on tokens, the token becomes a credential of the user. Hard credentials such as username and password are exchanged for a token that must be sent in each request then the server can perform authentication/authorization. Tokens can be valid for a short amount of time, can be revoked, can carry scope details (what can be requested with the token), etc.
使用令牌,您必須能夠識別以您的 API 為目標的用戶.因此,為所有經過身份驗證的用戶使用一個令牌是沒有意義的.
With a token, you must be able to identify the user who is targeting your API. Hence it makes no sense having a single token for all authenticated users.
使用 JWT 后,您可以使用用戶名進行聲明.還可以考慮為您的令牌添加過期日期 (exp代碼> 聲明).您不希望您的令牌永遠有效,是嗎?
Once you are using JWT, you could have a claim with the username. Also consider adding an expiration date for you token (exp
claim). You don't want your? token to be valid forever, do you?
使用 java-jwt,使用以下內容:
With java-jwt, use the following:
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
Date expirationDate = Date.from(ZonedDateTime.now().plusMinutes(60).toInstant());
String token = JWT.create()
.withExpiresAt(expirationDate)
.withClaim("username", username)
.sign(algorithm);
} catch (UnsupportedEncodingException e){
// UTF-8 encoding not supported
} catch (JWTCreationException e){
// Invalid signing configuration / Couldn't convert claims
}
驗證令牌時,您將能夠獲得 username
聲明并知道您為誰頒發令牌:
When verifying the token, you'll be able to get the username
claim and know who you issued the token for:
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
Claim usernameClaim = jwt.getClaim("username");
String username = usernameClaim.asString();
} catch (UnsupportedEncodingException e){
// UTF-8 encoding not supported
} catch (JWTVerificationException e){
// Invalid signature/claims
}
使用 JWT 處理令牌刷新
接受僅有效(未過期)令牌以進行更新.客戶有責任在 中指示的到期日期之前刷新令牌exp
聲明.
Handling token refreshment with JWT
Accept only valid (an non-expired) tokens for refreshment. It's responsability of the client to refresh the tokens before the expiration date indicated in the exp
claim.
為避免令牌無限期刷新,您可以通過向令牌添加兩個聲明來跟蹤令牌刷新(聲明名稱由您決定):
To avoid a token from being refreshed indefinitely, you could keep the track of the token refreshment by adding two claims to your token (the claim names are up to you):
refreshLimit
:表示token可以刷新多少次.refreshCount
:表示令牌刷新了多少次.
refreshLimit
: Indicates how many times the token can be refreshed.refreshCount
: Indicates how many times the token has been refreshed.
所以只有在滿足以下條件時才刷新令牌:
So only refresh the token if the following conditions are true:
- 令牌未過期(
exp >= now
). - 令牌被刷新的次數小于令牌可以被刷新的次數(
refreshCount
).
刷新令牌時:
- 更新到期日期(
exp = now + some-amount-of-time
). - 增加令牌刷新的次數 (
refreshCount++
).
一旦令牌被簽名并在服務器端驗證簽名,令牌的內容就不能被客戶端篡改.
Once the token is signed and the signature is verified on server side, the content of the token cannot be tampered by the client.
除了跟蹤茶點的數量之外,您還可以聲明絕對過期日期.在此日期之前,可以接受任意數量的茶點.
Alternatively to keeping? the track of the number of refreshments, you could have a claim that indicates the absolute expiration date. Before that date, any number of refreshments are acceptable.
另一種方法涉及發布一個單獨的長壽命刷新令牌,用于發布短壽命 JWT 令牌.
Another approach involves issuing a separate long-lived refresh token that is used to issue short-lived JWT tokens.
最佳方法取決于您的要求.
The best approach depends on your requirements.
如果您想撤銷令牌,您必須跟蹤它們.您不需要將整個令牌存儲在服務器端,只存儲令牌標識符(必須是唯一的)和一些元數據(如果需要).對于令牌標識符,您可以使用 UUID.
If you want to revoke tokens, you must keep the track of them. You don't need to store the whole token on server side, store only the token identifier (that must be unique) and some metadata if you need. For the token identifier you could use UUID.
jti
應使用聲明將令牌標識符存儲在令牌本身上.驗證令牌時,通過檢查 jti
聲明您在服務器端擁有的令牌標識符.
The jti
claim should be used to store the token identifier on the token itself. When validating the token, ensure that it has not been revoked by checking the value of the jti
claim against the token identifiers you have on server side.
出于安全考慮,請在用戶更改密碼時撤銷所有令牌.
For security purposes, revoke all the tokens for a user when they change their password.
有關 JAX-RS 中基于令牌的身份驗證的更多詳細信息,請參閱此答案.支持>
這篇關于JWT 發出相同的令牌的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!