因為我們要使用JWT,但在Spring Initializr中不能選擇jjwt,需要自行在pom.xml加入對應的dependency,在的下一行加入這些內容
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
在右上的部分會出現Load Maven Changes點擊它,或按下Ctrl+Shift+O,更新dependency的變化。
接著在Config建立JWTConstant.java,存放加密用的常數,可以自行設定,只要有32個以上的英文或數字組成就可以,就算是一個猴子沒有打出有意義的字母也能當SECRET。
public class JWTConstant {
public static final String SECRET = "Aeqt7zELDZnUSpy9Qr5mRNWdT3bK2ahF";
}
如果把JWT_CONSTANT直接寫在專案中可能有點風險,我們改成從.env載入,在pom.xml加入dependency。
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>java-dotenv</artifactId>
<version>5.2.2</version>
</dependency>
在.env加入
JWT_CONSTANT=Aeqt7zELDZnUSpy9Qr5mRNWdT3bK2ahF
修改JWTConstant.java
public class JWTConstant {
static Dotenv dotenv = Dotenv.load();
public static final String SECRET = dotenv.get("JWT_CONSTANT");
}
這樣就能從.env中讀取JWT_CONSTANT
在Config建立JWTProvider.java,用來設定JWT產生的內容。
用JWTConstant的SECRET產生驗證用的金鑰,JWT預設採用HMAC-SHA256,所以選擇產生HMAC-SHA256格式的金鑰。
@Service
public class JWTProvider {
SecretKey key = Keys.hmacShaKeyFor(JWTConstant.SECRET != null ? JWTConstant.SECRET.getBytes() : null);
設定產生的JWT裡面夾帶的內容,有token發行的時間、過期的時間,設定的時間單位是毫秒,在十二小時後到期,到期後的token將無法通過驗證。
再使用key來簽章,就能用來驗證內容有沒有被修改。
最後,將格式轉換成String。
public String generateToken(){
return Jwts.builder()
.setIssuedAt(new Date())
.setExpiration(new Date(new Date().getTime() + 12 * 60 * 60 * 1000))
.signWith(key)
.compact();
}
}
剛才已經設定好JWT了,接下來要從後端取得JWT。
我們先弄出一個註冊的架構,新增AuthController.java。
目前設定成讀取網頁,就能得到JWT。
@RestController
@RequestMapping("/auth")
public class AuthController {
private final JWTProvider jwtProvider;
public AuthController(JWTProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
@PostMapping("/signup")
public ResponseEntity<AuthResponse> createUserHandler(){
//使用JWTProvider產生token
String token = jwtProvider.generateToken();
AuthResponse authResponse = new AuthResponse();
authResponse.setJwt(token);
authResponse.setMessage("Signup Success");
return new ResponseEntity<>(authResponse, HttpStatus.CREATED);
}
}
由於ResponseEntity只能回傳一個內容或文字。
為了一次傳送兩個或以上的內容就需要新增一個class,並把想送出的內容包進去。
Response/AuthResponse.java的內容如下
public class AuthResponse {
private String jwt;
private String message;
public AuthResponse(){
}
public AuthResponse(String jwt, String message) {
this.jwt = jwt;
this.message = message;
}
public String getJwt() {
return jwt;
}
public void setJwt(String jwt) {
this.jwt = jwt;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
這樣我們就能在傳送JWT的同時,也送出Signup Success的訊息。
在這邊提供用來測試專案的設定,給大家下載
https://mega.nz/file/JNdF2C6L#eyb7KTmlDlAMRDASN_XZU_tOmGYtAK0InNBFojDQ8Yo
我們開啟API測試工具,POST到http://localhost:8080/auth/signup,就能得到jwt和Signup Success的訊息。
如果前一個章節不小心設定了CORS,那麽會顯示Invalid CORS request,請註解CORS的部分。
把JWT的部分複製起來,貼到https://jwt.io/#debugger-io
在VERIFY SIGNATURE的部分,輸入自己的JWT_CONSTANT,就能看到signature verified代表JWT的內容沒有遭到篡改。
JWT由三個部分組成:header、payload、signature。
header儲存採用的加密方式,雖然這邊沒有,header還可以儲存token的格式,例如:JWT。
payload用來放置發行和到期的相關內容,如果想在JWT夾帶一些資訊,也會放在這個部分,我們之後會夾帶email用來識別。
signature用來確認JWT有沒有被修改。