1.共享cookie實(shí)現(xiàn)單點(diǎn)登錄
1.1 單系統(tǒng)登錄的解決方案是cookie,cookie攜帶會(huì)話id在瀏覽器與服務(wù)器之間維護(hù)會(huì)話狀態(tài)。但是cookie是有限制的,這個(gè)限制就是cookie的域(通常對(duì)應(yīng)網(wǎng)站的域名),瀏覽器發(fā)送http請(qǐng)求時(shí)會(huì)自動(dòng)攜帶與該域匹配的cookie,而不是所有cookie。
1.2 共享cookie的方式存在眾多局限:
1)首先,應(yīng)用群域名得統(tǒng)一;
2)應(yīng)用群各系統(tǒng)使用的技術(shù)(至少是web服務(wù)器)要相同,不然cookie的key變量(tomcat為)不同,無法維持會(huì)話,共享cookie的方式是無法實(shí)現(xiàn)跨語言技術(shù)平臺(tái)登錄的,比如java、php、.net系統(tǒng)之間;
cookie本身不安全。
2.單點(diǎn)登錄SSO
2.1 什么是單點(diǎn)登錄:
單點(diǎn)登錄全稱Single Sign On(以下簡稱SSO),是指在多系統(tǒng)應(yīng)用群中登錄一個(gè)系統(tǒng),便可在其他所有系統(tǒng)中得到授權(quán)而無需再次登錄,包括單點(diǎn)登錄與單點(diǎn)注銷兩部分
2.2 sso登錄流程:
sso需要一個(gè)獨(dú)立的認(rèn)證中心,只有認(rèn)證中心能接受用戶的用戶名密碼等安全信息。sso認(rèn)證中心驗(yàn)證用戶的用戶名密碼沒問題,創(chuàng)建授權(quán)令牌,在接下來的跳轉(zhuǎn)過程中,授權(quán)令牌作為參數(shù)發(fā)送給各個(gè)子系統(tǒng),子系統(tǒng)拿到令牌,即得到了授權(quán),可以借此創(chuàng)建局部會(huì)話,局部會(huì)話登錄方式與單系統(tǒng)的登錄方式相同
2.3 sso注銷流程
單點(diǎn)登錄自然也要單點(diǎn)注銷,在一個(gè)子系統(tǒng)中注銷,所有子系統(tǒng)的會(huì)話都將被銷毀
用戶向系統(tǒng)1發(fā)起注銷請(qǐng)求
系統(tǒng)1根據(jù)用戶與系統(tǒng)1建立的會(huì)話id拿到令牌,向sso認(rèn)證中心發(fā)起注銷請(qǐng)求
sso認(rèn)證中心校驗(yàn)令牌有效,銷毀全局會(huì)話,同時(shí)取出所有用此令牌注冊(cè)的系統(tǒng)地址
sso認(rèn)證中心向所有注冊(cè)系統(tǒng)發(fā)起注銷請(qǐng)求
各注冊(cè)系統(tǒng)接收sso認(rèn)證中心的注銷請(qǐng)求,銷毀局部會(huì)話
sso認(rèn)證中心引導(dǎo)用戶至登錄頁面
3.JWT單點(diǎn)登錄
以上的session還有token的方案,在集群環(huán)境下,都是靠第三方緩存數(shù)據(jù)庫redis來實(shí)現(xiàn)數(shù)據(jù)的共享。
跟以上那些唯一的不同點(diǎn)就是:token存放了用戶的基本信息,更直觀一點(diǎn)就是將原本放入redis中的用戶數(shù)據(jù),放入到token中去了!
JWT相比session方案,因?yàn)閖son的通用性,所以JWT是可以進(jìn)行跨語言支持的,像JAVA、、PHP等很多語言都可以使用,而session方案只針對(duì)JAVA。
因?yàn)橛辛藀ayload部分,所以JWT可以存儲(chǔ)一些其他業(yè)務(wù)邏輯所必要的非敏感信息。
4.Gateway
1.使用Route結(jié)合Hystrix實(shí)現(xiàn)默認(rèn)降級(jí)策略
2.使用接口,自定義過濾器類,實(shí)現(xiàn)登錄態(tài)(token)校驗(yàn)
前端請(qǐng)求時(shí)path帶/gateway/,在gateway層使用=1,去掉gateway,最終微服務(wù)上的path不帶"/gateway/".
使用Hystrix實(shí)現(xiàn)默認(rèn)降級(jí)策略,降級(jí)接口實(shí)現(xiàn)如下:
@Slf4j
@RestController
public class DefaultHystrixController {
@RequestMapping("/defaultfallback")
public ApiResult defaultfallback(){
log.info("服務(wù)降級(jí)中");
return ApiResult.failure("服務(wù)異常");
}
}
4.1 實(shí)現(xiàn)登錄態(tài)(token)校驗(yàn)-自定義過濾器,實(shí)現(xiàn), Ordered 2個(gè)接口。
import com.*.auth.UserTokenTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Description token過濾器
*/
@Slf4j
@Component
public class LoginTokenFilter implements GatewayFilter, Ordered {
private static final String AUTHORIZE_TOKEN = "Authorization";
private static final String BEARER = "Bearer ";
/**
* token過濾
*

* @param exchange
* @param chain
* @return
*/
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("當(dāng)前環(huán)境已開啟token校驗(yàn)");
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
ServerHttpResponse response = exchange.getResponse();
// 取Authorization
String tokenHeader = headers.getFirst(AUTHORIZE_TOKEN);
log.info("tokenHeader=" + tokenHeader);
// token不存在
if (StringUtils.isEmpty(tokenHeader)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 取token
String token = this.getToken(tokenHeader);
log.info("token=" + token);
// token不存在
if (StringUtils.isEmpty(token)) {
log.info("token不存在");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 校驗(yàn) token是否失效
if (UserTokenTools.isTokenExpired(token, null)) {
log.info("token失效");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 校驗(yàn) token是否正確
if (!UserTokenTools.checkToken(token, null)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// //有token 這里可根據(jù)具體情況,看是否需要在gateway直接把解析出來的用戶信息塞進(jìn)請(qǐng)求中,我們最終沒有使用
// UserTokenInfo userTokenInfo = UserTokenTools.getUserTokenInfo(token);
// log.info("token={},userTokenInfo={}",token,userTokenInfo);
// request.getQueryParams().add("token",token);
//request.getHeaders().set("token", token);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -10;
}
/**
* 解析Token
*/
public String getToken(String requestHeader) {
//2.Cookie中沒有從header中獲取
if (requestHeader != null && requestHeader.startsWith(BEARER)) {
return requestHeader.substring(7);
}
return "";
}
}
4.2配置路由,可根據(jù)具體情況,如果只有一套登錄態(tài),那就用一個(gè)filter即可。
import com.*.gateway.filter.AuthorizeGatewayFilter;
import com.*.gateway.filter.LoginTokenFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator getRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// token校驗(yàn)1
.route(predicateSpec -> predicateSpec
.path("/gateway/pay/card/**", "/gateway/app/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new AuthorizeGatewayFilter()))
.uri("lb://OLOAN-PAY-SERVICE")
.id("OLOAN-PAY-SERVICE-token"))
// token校驗(yàn)2
.route(predicateSpec -> predicateSpec
.path("/gateway/order-audit/**", "/gateway/order/**", "/gateway/order-payment/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new LoginTokenFilter()))
.uri("lb://OLOAN-ORDER-SERVICE")
.id("OLOAN-ORDER-ORDER-token"))
.build();
}
}
yml參考
spring:
cloud:
gateway:
discovery:
locator:
enabled: false
#開啟小寫驗(yàn)證,默認(rèn)feign根據(jù)服務(wù)名查找都是用的全大寫
lowerCaseServiceId: true
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
routes:
- id: OLOAN-FINANCIAL-PRODUCT-SERVICE
# lb代表從注冊(cè)中心獲取服務(wù)
uri: lb://OLOAN-FINANCIAL-PRODUCT-SERVICE
predicates:
# 轉(zhuǎn)發(fā)該路徑
- Path=/gateway/financialProduct/**
# 帶前綴
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/defaultfallback
- id: ADMIN-SERVICE
uri: lb://ADMIN-SERVICE
predicates:
- Path=/gateway/auth/**
filters:
- StripPrefix=2
- name: Hystrix
args:
name: fallbackcmd
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。