はじめに
今回、IDとパスワードでのログインを実装しました。
また、ログイン後の画面に直接アクセスした場合、ログイン状態を確認し、遷移先を分岐させるようにしました。
ログイン
ログイン画面
<div class="container">
<h1>Login</h1>
<form (ngSubmit)="onSubmit()" #loginForm="ngForm">
<table>
<tr>
<td><label for="id">名前</label></td>
<td><input type="text" id="id" required [(ngModel)]="model.loginId" name="idName" (blur)="idValid()">
<span *ngIf="idVaridFlg">
名前を入力して下さい。
</span>
</td>
</tr>
<tr>
<td><label for="password">パスワード</label></td>
<td><input type="text" id="password" required [(ngModel)]="model.password" name="passName" (blur)="passwordValid()">
<span *ngIf="passwordVaridFlg">
パスワードを入力して下さい。
</span>
</td>
</tr>
</table>
<button type="submit" [disabled]="!loginForm.form.valid">ログイン</button>
<br /> {{model.message}}
</form>
<button (click)="define()">セッションの削除</button>
</div>
ログイン処理
簡単に説明すると、IDとパスワードをPOSTでバックエンド側にリクエストしています。
ステータス200が返ってきた場合は、次のページへ遷移させています。
onSubmit(): void {
this.auth();
}
auth(): void {
const myAsync = async (url) => {
const headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
const obj = this.model;
const body = JSON.stringify(obj);
const response = await fetch(url,{method: "POST",credentials: 'include', headers:headers,body:body}); //await で fetch() が完了するまで待つ
return response.status;
}
myAsync(this.Host + '/sessionsave').then((data)=>{
if(data == 200){
this.router.navigate(['members']);
}else{
this.model.message = 'ログインに失敗しました';
}
}).catch((err) => console.log('セッション情報の生成に失敗'));
}
ログイン処理でリクエストしているAPIです。
内容としては、ボディにセットされているIDとパスワードが正しい場合にセッション情報を作成しています。
また、今回はCookieも一応セットするようにしています。
補足ですが、Cookieとセッションは分けて考えたほうが良いです。
セッションもCookieを使用していますが、セッションIDをCookieに保存しています。
セッションで保存された情報はサーバ側にあります。また、セッションの情報はサーバ側で削除することもできます。
Cookieだけでもセッションを実現することはできますが、常にキーと値をフロントとバックを行き来することになるのと、その値は、デベロッパーツールで簡単に見ることができるので、セキュリティ的に弱いです。
例えば、ログアウトしてもCookieにキーと値をセットしてサーバ側に送信すればなりすましでログインできてしまいます。
DBを介せばCookieだけでもセッション管理できそうですが。。
@PostMapping("/sessionsave")
public void save(HttpServletResponse response, @RequestBody LoginInfo loginInfo) {
if(loginInfo.getLoginId().equals("×××") && loginInfo.getPassword().equals("×××")) {
Cookie cookie = new Cookie("key", "value");
response.addCookie(cookie);
session.setAttribute("key", cookie.getValue());
}
}
直アクセスへの対応(ガード処理)
ガード処理
バック側へセッションが有効かどうかをリクエストしています。
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
this.authCheck();
return true;
}
authCheck(): void {
const myAsync = async (url) => {
const response = await fetch(url,{credentials: 'include'});
const data = await response.json();
if(data.cookieValidFlag){
this.router.navigate(['members']);
}else{
this.router.navigate(['login']);
}
return data;
}
myAsync(this.Host + '/sessionload').then().catch((err) => console.log("データ取得に失敗"));
}
判定を入れたいコンポーネントにcanActivate: [AuthGuard]
を記載しています。
const routes: Routes = [
{path:'', redirectTo: '/login',pathMatch: 'full'},
{path:'login', component: LoginComponent},
{path:'members', component: MembersComponent, canActivate: [AuthGuard]}
]
java側の処理です。
セッションがnullでないことを確認しています。
nullでなければ、フロントに有効であることを返しています。
// セッションの取得
@GetMapping("/sessionload")
public CookieInfo load(
@CookieValue(name = "key", required = false) String value) {
String result = (String)session.getAttribute("key"); // 取得
CookieInfo cookieInfo = new CookieInfo();
cookieInfo.setCookieValidFlag(false);
if(result != null) {
cookieInfo.setCookieValidFlag(true);
}
return cookieInfo;
}
おわりに
どうでしたでしょうか。
かなり端折っていますが、重要な部分は記載できていると思います。
今回は触れていませんが、セッションIDをリクエストに付加して送る場合は、java側でフロントのURLを設定しておく必要がありました。
IDとパスワードはjavaのsrcに直接書いていますが、通常はDBに登録していると思うので、そちらは各々で変更してください。
もうセッションを使った認証は古いかもしれませんが、、良ければ参考程度に見てください。