1 シングル サインオン (SSO)
3 つの一般的な方法:
1.1 最初のタイプ: セッション ブロードキャスト メカニズムの実装 (廃止)
概念: セッション レプリケーション、モジュールがログインした後、モジュール セッションはユーザーのログイン情報を保存し、セッションを他のモジュールにコピーしてセッション ブロードキャストを実装し、すべてのモジュールがセッションを共有できるようにします; 欠点: モジュールが多い場合、セッション
を何度もコピーする必要があるため、リソースとメモリが無駄になります
1.2 2 番目のタイプ: cookie + redis を使用して達成する
コンセプト: プロジェクト内の任意のモジュールにログインし、ログイン後に redis と Cookie にデータを配置します。
1.2.1 ユーザーデータを保存するための redis+cookie
ステップ 1:
redis
キー: 一意のランダム値 (IP、ユーザー ID など)
値: ユーザーログインデータ
Cookie
を保存する redis の一意のキー値を Cookie に入れる
ステップ 2:
にアクセスするproject の他のモジュールが cookie を使用してリクエストを送信すると、サーバーはリクエストを受信し、cookie の値を取得し、redis に格納されている主キーを cookie から取得し、redis キーを介して対応するユーザー データをクエリします。空でない場合は、ログインしたことを意味します。
1.3 第三の方法: トークンを使って実現する
- プロジェクトの特定のモジュールにログインし、ログイン後に特定のルールに従って文字列 (トークン トークンを含むユーザー情報) を生成し、その文字列を返します。戻り方法は、Cookie またはアドレス バーを介して返すことができます。
- 次に、プロジェクトの他のモジュールにアクセスし、アクセスするたびにトークン トークン (アドレス バーにトークン パラメータを含む) を保持し、サーバーはアドレス バーから文字列 (トークン トークン) を取得し、トークンに従ってユーザー情報を取得します。 、結果が空でない場合は、すでにログインしています。
2 JWT(Json Webトークン)
従来のログイン認証方法:
2.1 セッション認証
概念: http プロトコル自体はステートレス プロトコルであるため、ユーザーがアカウント番号とパスワードを使用してシステムに認証された後、次のログイン要求は再度認証される必要があることを意味します。http プロトコルを介してどのユーザーがリクエストを行ったかを知ることはできないため、どのユーザーがリクエストを行ったかを知りたい場合は、ユーザー情報の一部をサーバーに保存 (セッションに保存) し、Cookie 値を返す必要があります。認証が成功した後. ブラウザーに渡すと、ユーザーは次のリクエストで Cookie 値を取得でき、サーバーは、リクエストを送信したユーザー、認証されているかどうか、ログインの有効期限が切れているかどうかなどを識別できます。
短所:セッションはサーバーに保存されるため、分散アプリケーションの場合、セッションが共有されないため、各マイクロサービスはユーザー認証を必要とします...
2.2 トークン認証
概念: このメソッドはセッション メソッドに似ています. 違いは, トークン値が redis に保存されることです. トークンは一般にランダムな文字列 (UUID など) であり, 値は一般にユーザー ID と有効期限です.時間が設定されています。サービスをリクエストするたびにリクエストヘッダーにトークンを入れてください. バックエンドがトークンを受け取ったら, トークンからredisが存在するかどうかを確認します. 存在する場合は, ユーザーが認証されたことを意味します. トークンが存在しない場合は,ログイン インターフェイスにジャンプして、ユーザーが再度ログインできるようにします. ログイン 成功後、トークン値をクライアントに返します。
複数のサーバーがトークンへのアクセスにredisを使用し、非共有の問題がないため、拡張が容易であるという利点があります。不利な点は、各リクエストがredisをチェックする必要があることです, これによりredisに圧力がかかり、リクエストの消費時間が長くなります. ログインしている各ユーザーはトークンをredisに保存する必要があり, これもredisのストレージスペースを消費します.
2.3 JWT 認証
トークンは一定のルールに従って文字列を生成するものであり、JWT は公式が推奨する一般的なルールであり、JWT ルールを使用して文字列を生成できます。
JWT によって生成される文字列には 3 つの部分が含まれます。
最初の部分: JWT ヘッダー情報。これは、JWT メタデータを記述する Json オブジェクトです。例:
{ "alg": "HS256", //algorithm "typ": "JWT" // トークンの種類} 2 番目の部分: ペイロード。渡す必要があるデータ (ユーザー情報) を含むことに加えて、選択用の 7 つのデフォルト フィールドがあります。{ //デフォルト フィールド"sub": "Subject 123", //カスタム フィールド"name": "Java technology lover", "isAdmin": "true", "loginTime": "2021-12-05 12:00: 03” } 3 番目の部分: 署名 (偽造防止マーク) は、最初に秘密を指定する必要があります。秘密はサーバーにのみ保存され、他のユーザーに知られないように保証されます。次に、ヘッダーで指定されたアルゴリズムを使用してヘッダーとペイロードを計算し、署名である署名ハッシュを取得します。では、どうやって確認するのでしょうか?JWT の最初の 2 つのセクションを使用して、同じハッシュ アルゴリズムのセットと同じシークレットで署名値を計算し、計算された署名値を受信した JWT の 3 番目のセクションと比較できます。認証が通過します。
2.3.1 JWT の使用
(1) 依存関係を導入する:
<!-- JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
(2) JWT ツール クラスを作成する
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @author
*/
public class JwtUtils {
public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
3 Alibaba Cloud ショート メッセージ サービス (SMS)
3.1 Alibaba Cloud SMS サービスに基づく SMS 認証コードの送受信
Alibaba Cloud の SMS サービスを使用する場合は、最初にサービスを有効にする必要があり、署名とテンプレートが必要です (署名は SMS のタイトルで、通常は企業または関連する単位です。テンプレートは特定のコンテンツです)。 SMSの)。このサービスは、請求回数に応じて数セント課金されます。
しかし、現在、署名とテンプレートの申請には、会社の資格証明書と登録されたウェブサイトのアドレスを提供する必要があります。
SMS 検証コードの送受信など、Alibaba Cloud の SMS サービスをテストするだけの場合、Alibaba Cloud は無料のテスト API を提供します。
Alibaba Cloud が提供する SDK の例によると、検証コード メソッドを実現するために独自のコードに統合できます。
3.1.1 Alibaba Cloud SMS 検証コード テスト API の例
依存関係のインポート:
<!-- 阿里云短信服务测试API依赖 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>2.2.1</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
サービス:
package com.zhmsky.msmService.service;
/**
* @author zhmsky
* @date 2022/7/6 21:00
*/
public interface MsmService {
/**
* 阿里云发送短信验证码
* @param phone 接收验证码的手机号
* @param code 被发送的验证码
* @return
*/
String sendCodeMsg(String phone,String code);
}
サービス実装:
package com.zhmsky.msmService.service.impl;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.zhmsky.msmService.service.MsmService;
import org.springframework.stereotype.Service;
/**
* @author zhmsky
* @date 2022/7/6 21:01
*/
@Service
public class MsmServiceImpl implements MsmService {
/**
* 发送短信验证码
* @param phone 手机号
* @param code 被发送的验证码
* @return
*/
@Override
public String sendCodeMsg(String phone, String code) {
String checkCode="";
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "LTAI5tDfHQPQ5WA5dBkrfFxu", "eRID7ZigveAH7fMRKbCDq92jjRl68R");
IAcsClient client = new DefaultAcsClient(profile);
SendSmsRequest request = new SendSmsRequest();
request.setSignName("阿里云短信测试");
request.setTemplateCode("SMS_154950909");
request.setPhoneNumbers(phone);
request.setTemplateParam("{\"code\":\""+code+"\"}");
try {
SendSmsResponse response = client.getAcsResponse(request);
checkCode = response.getCode();
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
System.out.println("ErrCode:" + e.getErrCode());
System.out.println("ErrMsg:" + e.getErrMsg());
System.out.println("RequestId:" + e.getRequestId());
}
return checkCode;
}
}
インターフェイス テストの例:
import com.zhmsky.msmService.MsmApplication;
import com.zhmsky.msmService.service.MsmService;
import com.zhmsky.msmService.utils.RandomUtil;
import javafx.application.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author zhmsky
* @date 2022/7/6 23:58
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MsmApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MainTest {
@Autowired
private MsmService msmService;
@Test
public void msgSendTest(){
String sixBitRandom = RandomUtil.getSixBitRandom();
String s = msmService.sendCodeMsg("你的手机号(需要在阿里云短信服务测试API绑定)", sixBitRandom);
if("OK".equals(s)){
System.out.println("发送成功!");
}else {
System.out.println("发送失败!");
}
}
}
対応するリクエスト レスポンスの本文は、API ドキュメントで確認できます。