微服務(wù)網(wǎng)關(guān):spring cloud gateway實(shí)現(xiàn)jwt統(tǒng)一認(rèn)證
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|??堅(jiān)持堅(jiān)持
來源 |? urlify.cn/VjIZJ3
66套java從入門到精通實(shí)戰(zhàn)課程分享
當(dāng)你的項(xiàng)目中服務(wù)越來越多,每個(gè)服務(wù)都有自己的監(jiān)聽地址而又需要把這些服務(wù)提供給各式的客戶端或第三方使用,那么需要把每個(gè)服務(wù)地址都暴露出來嗎?如果某個(gè)服務(wù)有多個(gè)運(yùn)行實(shí)例,如果進(jìn)行負(fù)載均衡?用戶認(rèn)證和授權(quán)需要在每個(gè)服務(wù)上都做嗎,能否統(tǒng)一做?要解決這些問題,就需要用到Api網(wǎng)關(guān),Api網(wǎng)關(guān)提供Api請求轉(zhuǎn)發(fā)服務(wù)并可與Eureka結(jié)合實(shí)現(xiàn)路由轉(zhuǎn)發(fā)和負(fù)載均衡,同時(shí)利用AOP特性可以實(shí)現(xiàn)微服務(wù)用戶統(tǒng)一認(rèn)證和授權(quán)。目前比較流行的Api網(wǎng)關(guān)有Zuul、springcloud gateway以及支持dotnetcore的ocelot。本文使用的是springcloud。
一,搭建springcloud gateway
1,新建一個(gè)springboot項(xiàng)目,引入eureka-client和stater-gateway
????
????????org.springframework.cloud
????????spring-cloud-starter-gateway
????
????
????????org.springframework.cloud
????????spring-cloud-starter-netflix-eureka-client
????
????
????????org.springframework.boot
????????spring-boot-starter-test
????????test
????????
????????????
????????????????org.junit.vintage
????????????????junit-vintage-engine
????????????
????????
????
2,項(xiàng)目配置文件:Application.yml
server:
??port:?8020
spring:
??application:
????name:?gateway-server
??cloud:
????gateway:
??????discovery:
????????locator:
??????????enabled:?true?//啟用網(wǎng)關(guān)服務(wù)發(fā)關(guān)
??????????lower-case-service-id:?true?//支持小寫字母的服務(wù)名稱(注冊到eureka的服務(wù))
ek:
??username:?eureka
??password:?123456
??eureka-url:?127.0.0.1
??eureka-port:?8765
eureka:
??client:
????service-url:
??????defaultZone:
????????http://${ek.username}:${ek.password}@${ek.eureka-url}:${ek.eureka-port}/eureka??//Eureka注冊地址
??instance:
????prefer-ip-address:?true??//啟用優(yōu)先IP地址注冊
????instance-id:?${spring.application.name}@${spring.cloud.client.ip-address}@${server.port}?//本網(wǎng)關(guān)注冊到Eureka的實(shí)例id
3,啟用項(xiàng)目注冊到Eureka,并通過網(wǎng)關(guān)訪問Api

?
?
?驗(yàn)證Api

?二,DotnetCore JWT證書頒布服務(wù)
1,新建一個(gè)web api項(xiàng)目,Nuget添加:System.IdentityModel.Tokens.Jwt、Microsoft.IdentityModel.Tokens兩個(gè)包。配置文件添加jwt配置信息。
appsetting.json 注意:Key用于jwt的簽名,長度不能少于16位。可用openssl生成一個(gè)32位的密鑰。
"Identity":?{
???"Jwt":?{
?????"Key":?"1234567890123456",
?????"Domain":?"kingsun.mico"
???}
?}
使用IOptions讀取配置信息并寫入依賴
Startup.cs
public?void?ConfigureServices(IServiceCollection?services)
????{
????????services.Configure(Configuration.GetSection("Identity"));
????????services.AddControllers();
????}
2,新建一個(gè)名為Token的Api控制器并創(chuàng)建接口GetToken
[Route("api/[controller]")]
????[ApiController]
????public?class?TokenController?:?ControllerBase
????{
????????JwtConfig?config;
????????public?TokenController(IOptions?config)
????????{
????????????this.config?=?config.Value;
????????}
????????public?class?GetTokenRequest
????????{
????????????[Required]
????????????public?string?Name?{?get;?set;?}
????????????[Required]
????????????public?string?Password?{?get;?set;?}
????????}
????????public?class?GetTokenRespone
????????{
????????????public?int?Code?{?get;?set;?}
????????????public?string?Msg?{?get;?set;?}
????????????public?string?Data?{?get;?set;?}
????????}
????????[HttpPost("GetToken")]
????????public?object?GetToken([FromBody]?GetTokenRequest?data)
????????{
????????????GetTokenRespone?respone?=?new?GetTokenRespone()
????????????{
????????????????Code?=?0
????????????};
????????????if?(!ModelState.IsValid)
????????????{
????????????????respone.Msg?=?"用戶名或密碼不能為空";
????????????????return?respone;
????????????}
????????????//驗(yàn)證密碼邏輯
????????????//....
?
????????????//jwt中payload鍵值對內(nèi)容
????????????List?claims?=?new?List()
????????????{
????????????????//用戶名
????????????????new?Claim("name",data.Name)
????????????};
????????????var?key?=?new?SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(config.Jwt.Key));
????????????var?creds?=?new?SigningCredentials(key,SecurityAlgorithms.HmacSha256);
????????????var?token?=?new?JwtSecurityToken(
?????????????????issuer:?"liujb",?audience:?config.Jwt.Domain,?claims:?claims,?signingCredentials:?creds,?expires:DateTime.Now.AddDays(1),notBefore:DateTime.Now
????????????????);
????????????respone.Code?=?1;
????????????respone.Msg?=?"請求成功";
????????????respone.Data?=?new?JwtSecurityTokenHandler().WriteToken(token);
????????????return?respone;
????????}
????}
3,獲取jwt令牌

?
?
?可拿此令牌驗(yàn)證后內(nèi)容如下:

?
?
4,將此服務(wù)注冊到Eureka后用api網(wǎng)關(guān)轉(zhuǎn)發(fā)服務(wù)獲取token:http://localhost:8020/eureka-identity/api/token/gettoken
{
????"code":?1,
????"msg":?"請求成功",
????"data":?"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoibGl1amIiLCJuYmYiOiIxNjAyODE3NjY1MjMwIiwiZXhwIjoiMTYwMjkwNDA2NTIzMCIsImlzcyI6ImxpdWpiIiwiYXVkIjoia2luZ3N1bi5taWNvIn0.5pIrNumEEy280-sCWp4d0K05Skc2Ptn1sK732Rw-L-A"
}
三,在api網(wǎng)關(guān)統(tǒng)一做接口認(rèn)證
1,在第一步建立的網(wǎng)關(guān)項(xiàng)目中maven加入java-jwt依賴
???????????com.auth0
???????????java-jwt
???????????3.11.0
???????
配置文件加入jwt密鑰:jwt.key=1234567890123456。值與第二步的中密鑰一致。
2,在該項(xiàng)目新建一個(gè)全局過濾器。
@Component
public?class?JwtFilter?implements?GlobalFilter,?Ordered?{
????@Value("${jwt.key}")
????public?String?jwtKey;
????Logger?logger=null;
????public?JwtFilter(){
????????logger=?LoggerFactory.getLogger("JwtFilter");
????}
????@Override
????public?Mono?filter(ServerWebExchange?exchange,?GatewayFilterChain?chain)?{
????????String?requestUrl=exchange.getRequest().getPath().toString();
????????ServerHttpResponse?response?=?exchange.getResponse();
????????/*過濾掉獲取token的接口*/
????????if(requestUrl.toLowerCase().equals("/eureka-identity/api/token/gettoken")){
????????????return?chain.filter(exchange);
????????}
????????//獲取token
????????String?token=exchange.getRequest().getHeaders().getFirst("Authorization");
????????//沒有token返回未認(rèn)證
????????if(token==null||token==""){
????????????response.setStatusCode(HttpStatus.UNAUTHORIZED);
????????????return?response.setComplete();
????????}
????????try{
????????????this.getJwtByToken(token,jwtKey,null);
????????????return?chain.filter(exchange);
????????}
????????catch(Exception?ex){
????????????logger.error(ex.getMessage());
????????????response.setStatusCode(HttpStatus.UNAUTHORIZED);
????????????return?response.setComplete();
????????}
????}
?
????@Override
????public?int?getOrder()?{
????????return?0;
????}
????private?DecodedJWT?getJwtByToken(String?token,?String?key,?Map?claims)?throws?Exception{
????????Algorithm?algorithm?=?Algorithm.HMAC256(key);
????????Verification?verifier=?JWT.require(algorithm)
????????????????.withIssuer("liujb");
????????if(claims!=null){
????????????claims.forEach((mapKey,mapValue)->{
????????????????verifier.withClaim(mapKey,mapValue);
????????????});}
????????JWTVerifier?ver=?verifier.build();
????????DecodedJWT?jwt=ver.verify(token);
????????return?jwt;
????}
}
這里只做了簡單的認(rèn)證,以此基礎(chǔ)進(jìn)行深化,如根據(jù)不同的服務(wù)名稱要求不同的claim。
3,測試
加入Authorization關(guān),值為之前獲取的jwt token

?
?如果token不對或沒有token都將返回401狀態(tài)值?

?
粉絲福利:108本java從入門到大神精選電子書領(lǐng)取
???
?長按上方鋒哥微信二維碼?2 秒 備注「1234」即可獲取資料以及 可以進(jìn)入java1234官方微信群
感謝點(diǎn)贊支持下哈?
