余談
https://qiita.com/yniji/items/56a86283ddef245bc35f
に記載しているように、プログラミング学習は文法の土台が先決という常識が崩れている
そのため、ガンガン設計を重視した学習を進めていく
JWTの認証サーバー
作成するのは3つのファイルのみでできてしまった
DBアクセスはなしのためハードコーディングした
@RestController
@RequestMapping("/auth")
public class AuthController {
private final TokenService tokenService;
public AuthController(TokenService tokenService) {
this.tokenService = tokenService;
}
record LoginRequest(String userName ,String password){}
record LoginResponse(String access_token, long expires_in) {}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
// 学習用: ユーザー固定
if ("user".equals(req.userName()) && "pass".equals(req.password())) {
String token = tokenService.issue(req.userName());
System.out.println("ssss");
return ResponseEntity.ok(new LoginResponse(token, 3600));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("invalid credentials");
}
}
curlでたたいたら見事、認証成功、認証失敗が出るようになる!!
その他インプット
ResponseEntity
レスポンスのHttpステータスとbodyを返却できる
ResponseEntity.ok(new LoginResponse(token, 3600));
のように書くと200番で以下のようなbodyを返却できるようですね
{
"accessToken": "xxxxxx",
"expiresIn": 3600
}
また、OKは200番の省略形で他のステータスを返却する場合は以下のような書き方ができる
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body("ログインが必要です");
一覧
ステータス | 意味 | コード例 |
---|---|---|
200 OK | 成功 | return ResponseEntity.ok(obj); |
201 Created | 新規作成成功 | return ResponseEntity.status(HttpStatus.CREATED).body(obj); |
204 No Content | 成功だが本文なし | return ResponseEntity.noContent().build(); |
400 Bad Request | リクエスト不正 | return ResponseEntity.badRequest().body("エラー"); |
401 Unauthorized | 認証失敗 | return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("ログインしてください"); |
403 Forbidden | 権限なし | return ResponseEntity.status(HttpStatus.FORBIDDEN).body("権限がありません"); |
404 Not Found | データなし | return ResponseEntity.status(HttpStatus.NOT_FOUND).body("見つかりません"); |
500 Internal Server Error | サーバー側のエラー | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("サーバーエラー"); |
また、これらを呼び出すAPIなどでレスポンスのHTTPステータスによって処理を分岐させるのは大変であるため以下のようにControllerでの共通エラーハンドリングを行うことができる
@RestControllerAdvice
public class GlobalRestExceptionHandler {
@ExceptionHandler(HttpClientErrorException.NotFound.class)
public Map<String, Object> handleNotFound(HttpClientErrorException.NotFound e) {
Map<String, Object> error = new HashMap<>();
error.put("status", 404);
error.put("message", "データが見つかりません");
return error;
}
}
JWTの発行(jwts.build)
return Jwts.builder()
.subject(userName) // トークンの「誰?」を設定
.claim("role", "USER") // 追加情報(クレーム)を入れる
.issuedAt(Date.from(now)) // 発行日時
.expiration(Date.from(now.plusSeconds(300L))) // 有効期限(300秒後)
.signWith(key) // 署名を付ける(改ざん防止)
.compact(); // 最終的に文字列(JWT)に変換
そもそもJWTの構成としては以下のようになっており
aaaaa.bbbbb.ccccc
1.Header(署名方式など)
2.Payload(sub, role などのクレーム情報)
3.Signature(改ざん検出用)
となっている!