詳しくはJWTでSprigCloudを認証し、認証する。
13419 ワード
JWT(JSON WEB TOKEN)は、RFC 7519規格に基づいて定義された、安全に伝送できるコンパクトさと、自己が含むJSONオブジェクトである。データはデジタル署名を使用していますので、信頼性と安全性があります。JWTは、HMACアルゴリズムを使用してsecretを暗号化したり、RSAの公開鍵秘密鍵ペアを使用して署名したりすることができる。
JWTは通常ヘッド、ロード、署名の三つの部分から構成され、中間は.号で分離され、そのフォーマットはHeader.Payload.Signatureである。
Header:トークンの種類と使用アルゴリズムを宣言する。 alg:署名のアルゴリズム typ:tokenのタイプ、例えばJWT Payload:JWT Cliaimsともいいます。ユーザーの情報を含みます。
システムが保持している宣言(Reserved claims): iss:署名者 exp(expiration time):賞味期限 sub(subject):テーマ aud(audiience):アクセプター nbf(Not Before):これまでは は利用できません。 iat(Issued At):署名時間 jti(JWT ID):JWT固有の識別は、JWTが を繰り返し使用することを防止するために使用できます。
公的声明(public):http://www.iana.org/assignments/jwt/jwt.xhtmlを参照してください。
プライベートステートメント(prvate claims):ビジネスニーズに応じて自分で定義したデータ
Signature:署名
署名のフォーマット: HMACSHA256(base 64 UrlEnccode+「.」+base 64 UrlEnccode、secret)
JWTの特徴: JWTはデフォルトでは暗号化されていないので、ユーザーの敏感な情報をPayloadの部分に置くことはできません。 JWTは認証のためだけでなく、情報交換のためにも利用できます。 JWTの最大の欠点は、サーバがセッション状態を保存しないことであるので、使用中にトークンをキャンセルしたり、トークンの権限を変更したりすることは不可能である。 JWT自身は認証情報を含んでいます。盗用を減らすために、JWTの有効期間は長く設定するべきではありません。 は、盗用と盗用を低減するために、JWTはHTTPプロトコルを使用してコードを送信することを提案しない代わりに、暗号化されたHTTPSプロトコルを用いて伝送する。 初めてtokenを生成するのは比較的に遅く、CPUを消耗し、高合併の場合はCPU占有問題を考慮する必要がある。 によって生成されたtokenは比較的に長く、流量問題を考慮する必要があるかもしれない。 認証の原理:クライアントはサーバにライセンスを申請し、サーバ認証後、token文字列を生成してクライアントに戻り、その後クライアントは を要求する。保護されたリソースは、このtokenを携帯し、サービス側で検証し、このtokenからユーザのアイデンティティ情報を解析する。 JWTの使い方:HTTP要求のヘッダ情報Authorzationフィールドに入れる方法で、フォーマットは以下の通りである。
もう一つの方法は、ドメインをまたぐ時、JWTはPOSTが要求したデータ体に入れます。
JWTに対してtoken更新を実現する方法:
1、新たなtokenを取得するためにrefshTokenを追加的に生成し、refresh Tokenはサービス端末に保存する必要があり、その期限がJWTの期限切れより少し長い。
2、ユーザはrefreshTokenパラメータ要求tokenを携帯してインターフェースを更新し、サービス端末はrefshTokenが期限切れしていないと判断した後、関連するユーザ情報と現在tokenを取り出す。
3、現在のユーザ情報を用いてtokenを再生成し、古いtokenをブラックリストにセットして、新しいtokenを返す。
登録認証用のプロジェクトauth-serviceを作成します。
1、pom.xmlファイルを作成する
1、pom.xmlファイルに依存を追加する
JWTは通常ヘッド、ロード、署名の三つの部分から構成され、中間は.号で分離され、そのフォーマットはHeader.Payload.Signatureである。
Header:トークンの種類と使用アルゴリズムを宣言する。
システムが保持している宣言(Reserved claims):
公的声明(public):http://www.iana.org/assignments/jwt/jwt.xhtmlを参照してください。
プライベートステートメント(prvate claims):ビジネスニーズに応じて自分で定義したデータ
Signature:署名
署名のフォーマット: HMACSHA256(base 64 UrlEnccode+「.」+base 64 UrlEnccode、secret)
JWTの特徴:
Authorization: <token>
すべてのドメインからの要求を受け入れるようにサーバーを設定する必要があります。Access-Coontrol-Origin:*もう一つの方法は、ドメインをまたぐ時、JWTはPOSTが要求したデータ体に入れます。
JWTに対してtoken更新を実現する方法:
1、新たなtokenを取得するためにrefshTokenを追加的に生成し、refresh Tokenはサービス端末に保存する必要があり、その期限がJWTの期限切れより少し長い。
2、ユーザはrefreshTokenパラメータ要求tokenを携帯してインターフェースを更新し、サービス端末はrefshTokenが期限切れしていないと判断した後、関連するユーザ情報と現在tokenを取り出す。
3、現在のユーザ情報を用いてtokenを再生成し、古いtokenをブラックリストにセットして、新しいtokenを返す。
登録認証用のプロジェクトauth-serviceを作成します。
1、pom.xmlファイルを作成する
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.seasy.springcloud</groupId>
<artifactId>auth-service</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.8.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2、JWTツール類
public class JWTUtil {
public static final String SECRET_KEY = "123456"; //
public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token
public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken
private static final String ISSUER = "issuer"; //
/**
*
*/
public static String generateToken(String username){
Date now = new Date();
Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //
String token = JWT.create()
.withIssuer(ISSUER) //
.withIssuedAt(now) //
.withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //
.withClaim("username", username) //
.sign(algorithm);
return token;
}
/**
* token
*/
public static boolean verify(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(ISSUER)
.build();
verifier.verify(token);
return true;
} catch (Exception ex){
ex.printStackTrace();
}
return false;
}
/**
* token username
*/
public static String getUsername(String token){
try{
return JWT.decode(token).getClaim("username").asString();
}catch(Exception ex){
ex.printStackTrace();
}
return "";
}
}
3、Login Controller類
@RestController
public class LoginController {
@Autowired
StringRedisTemplate redisTemplate;
/**
*
* @param username
* @param password
*/
@GetMapping("/login")
public AuthResult login(@RequestParam String username, @RequestParam String password) {
if("admin".equals(username) && "admin".equals(password)){
// token
String token = JWTUtil.generateToken(username);
// refreshToken
String refreshToken = StringUtil.getUUIDString();
// redis
redisTemplate.opsForHash().put(refreshToken, "token", token);
redisTemplate.opsForHash().put(refreshToken, "username", username);
// token
redisTemplate.expire(refreshToken, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
return new AuthResult(0, "success", token, refreshToken);
}else{
return new AuthResult(1001, "username or password error");
}
}
/**
* token
*/
@GetMapping("/refreshToken")
public AuthResult refreshToken(@RequestParam String refreshToken) {
String username = (String)redisTemplate.opsForHash().get(refreshToken, "username");
if(StringUtil.isEmpty(username)){
return new AuthResult(1003, "refreshToken error");
}
// token
String newToken = JWTUtil.generateToken(username);
redisTemplate.opsForHash().put(refreshToken, "token", newToken);
return new AuthResult(0, "success", newToken, refreshToken);
}
@GetMapping("/")
public String index() {
return "auth-service: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
4、アプリの設定情報
spring.application.name=auth-service
server.port=4040
eureka.instance.hostname=${spring.cloud.client.ip-address}
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
eureka.instance.prefer-ip-address=true
eureka.client.service-url.defaultZone=http://root:123456@${eureka.instance.hostname}:7001/eureka/
#redis
spring.redis.database=0
spring.redis.timeout=3000ms
spring.redis.lettuce.pool.max-active=100
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8
#standalone
spring.redis.host=192.168.134.134
spring.redis.port=7001
#sentinel
#spring.redis.sentinel.master=mymaster
#spring.redis.sentinel.nodes=192.168.134.134:26379,192.168.134.134:26380
5、起動クラス
@SpringBootApplication
@EnableEurekaClient
public class Main{
public static void main(String[] args){
SpringApplication.run(Main.class, args);
}
}
SprigCloud Gatewayを改造するプロジェクト1、pom.xmlファイルに依存を追加する
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
2、グローバルフィルタJWT AuthFilterを作成する
@Component
public class JWTAuthFilter implements GlobalFilter, Ordered{
@Override
public int getOrder() {
return -100;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String url = exchange.getRequest().getURI().getPath();
// url
if(url.indexOf("/auth-service/") >= 0){
return chain.filter(exchange);
}
// token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if(StringUtil.isEmpty(token)){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Response res = new Response(401, "401 unauthorized");
byte[] responseByte = JSONObject.fromObject(res).toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(responseByte);
return response.writeWith(Flux.just(buffer));
}
// token redis
boolean verifyResult = JWTUtil.verify(token);
if(!verifyResult){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Response res = new Response(1004, "invalid token");
byte[] responseByte = JSONObject.fromObject(res).toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(responseByte);
return response.writeWith(Flux.just(buffer));
}
return chain.filter(exchange);
}
}
3、キーのアプリ配置情報
spring:
application:
name: service-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lowerCaseServiceId: true
routes:
#
- id: auth-service
predicates:
- Path=/auth-service/**
uri: lb://auth-service
filters:
- StripPrefix=1
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。