SSO單點登入
1、單一伺服器模式
- 使用者向伺服器傳送使用者名稱和密碼。
- 驗證伺服器後,相關資料(如使用者角色,登入時間等)將儲存在當前會話中。
- 伺服器向使用者返回session_id,session資訊都會寫入到使用者的Cookie。
- 使用者的每個後續請求都將通過在Cookie中取出session_id傳給伺服器。
- 伺服器收到session_id並對比之前儲存的資料,確認使用者的身份。
2、SSO(single sign on)模式
隨著業務擴大,業務分開部署,儲存在session中時無法在另一臺伺服器中判斷登入。
2.1、session廣播
- 將同伺服器的session複製到其他伺服器中
缺點
:資料冗餘,增加伺服器的負擔
2.2、cookie+redis
- 登入之後資料儲存在兩個地方;Redis中Key生成使用者的唯一id,value值儲存使用者資訊,瀏覽器將Redis生成的唯一id儲存在cookie中。
- 訪問其他模組,傳送請求時攜帶cookie,取出cookie中的id對redis操作判斷是否登入。
優點
: 使用者身份資訊獨立管理,更好的分散式管理; 可以自己擴充套件安全策略
缺點
:認證伺服器訪問壓力較大
- Spring-session實現全域性session儲存
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- application.properties
server.port=9000
spring.application.name=spring-session
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0
- 開啟spring-session
@EnableRedisHttpSession
@SpringBootApplication
public class SsoApplication {
public static void main(String[] args) {
SpringApplication.run(SsoApplication.class, args);
}
}
- Controller
@GetMapping("put")
public String putSession(HttpSession session){
session.setAttribute("user","登入資訊");
return "ok";
}
@GetMapping("get")
public String getSession(HttpSession session){
Object user = session.getAttribute("user");
return "ok"+user;
}
- redis
#keys *
1) "spring:session:expirations:1602219480000"
2) "spring:session:sessions:expires:d131e843-75f6-4c9c-97ab-a0a884a95a28"
3) "spring:session:sessions:d131e843-75f6-4c9c-97ab-a0a884a95a28"
- 自定義序列化
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<Object,Object> redisTemplate =new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
//Jackson序列化value
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(mapper);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory){
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
- 原理
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);//請求包裝
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response);//響應包裝
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);//包裝過的請求響應通過過濾器
}
finally {
wrappedRequest.commitSession();
}
}
2.3、token
- 登入之後,根據使用者資訊按照一定的規則生成字串傳送給前端。
- 訪問其他模組時,使用cookie攜帶token或位址列引數攜帶token傳送請求,後端收到token值進行解析判斷登入
優點
:無狀態: token無狀態,session有狀態的;基於標準化: 可以採用標準化的 JSON Web Token (JWT)
缺點
:佔用頻寬;無法在伺服器端銷燬
- JWT儲存登入資訊
- JWT頭:描述JWT後設資料的JSON物件
alg屬性
表示簽名使用的演算法,預設為HMAC SHA256(寫為HS256);typ屬性
表示令牌的型別,JWT令牌統一寫為JWT。- 最後,使用
Base64 URL演算法
將JSON物件轉換為字串儲存
{
"alg": "HS256",
"typ": "JWT"
}
- JWT主體:有效載荷部分,是JWT的主體內容部分,也是一個JSON物件,包含需要傳遞的資料。
請注意
,預設情況下JWT是未加密的,任何人都可以解讀其內容,因此不要構建隱私資訊欄位,存放保密信息,以防止資訊洩露。JSON物件也使用Base64 URL演算法轉換為字串儲存。
預設提供欄位:
#iss:發行人
#exp:到期時間
#sub:主題
#aud:使用者
#nbf:在此之前不可用
#iat:釋出時間
#jti:JWT ID用於標識該JWT
自定義欄位:
{
"sub": "1234567890",
"name": "Helen",
"admin": true
}
- JWT簽名:簽名雜湊部分是對上面兩部分資料簽名,通過指定的演算法生成雜湊,以確保資料不會被篡改。
- 首先,需要指定一個密碼(secret)。該密碼僅僅為儲存在伺服器中,並且不能向使用者公開。
- 然後,使用標頭中指定的簽名演算法(預設情況下為HMAC SHA256)根據以下公式生成簽名。
- JWT問題和趨勢
-
JWT不僅可用於認證,還可用於資訊交換。善用JWT有助於減少伺服器請求資料庫的次數。
-
生產的token可以包含基本資訊,比如id、使用者暱稱、頭像等資訊,避免再次查庫
-
儲存在客戶端,不佔用服務端的記憶體資源
-
JWT預設不加密,但可以加密。生成原始令牌後,可以再次對其進行加密。
-
當JWT未加密時,一些私密資料無法通過JWT傳輸。
-
JWT的最大缺點是伺服器不儲存會話狀態,所以在使用期間不可能取消令牌或更改令牌的許可權。也就是說,
一旦JWT簽發,在有效期內將會一直有效。
-
JWT本身包含認證資訊,token是經過base64編碼,所以可以解碼,因此token加密前的物件不應該包含敏感資訊,一旦資訊洩露,任何人都可以獲得令牌的所有許可權。為了減少盜用,JWT的有效期不宜設定太長。對於某些重要操作,使用者在使用時應該每次都進行進行身份驗證。
-
為了減少盜用和竊取,JWT不建議使用HTTP協議來傳輸程式碼,而是使用加密的HTTPS協議進行傳輸。
- JWT工具類
public class JwtUtils {
//過期時間
public static final long EXPIRE = 1000 * 60 * 60 * 24;
//伺服器金鑰
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
//生成token
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
//頭資訊
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//預設主體
.setSubject("user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//自定義主體
.claim("id", id)
.claim("nickname", nickname)
//簽名hash
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
// 判斷token是否存在與有效
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是否存在與有效
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
public static String getIdByReq(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");
}
//根據token獲取會員id
public static String getIdByJwt(String token) {
if(StringUtils.isEmpty(token)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
- controller
@GetMapping("token/put")
public String putSession(){
return JwtUtils.getJwtToken("1", "wdd");
}
//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
//eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNjAyMjIyNzQ0LCJleHAiOjE2MDIzMDkxNDQsImlkIjoiMSIsIm5pY2tuYW1lIjoid2RkIn0.
//LJyXdXttVyd1xI91QetkcWKd3WhuNiaJs3aLWy8RqcY
@GetMapping("token/get")
public String getSession(@RequestParam("token")String token){
return JwtUtils.getIdByJwt(token);
}
相關文章
- SSO 單點登入
- 單點登入(SSO)
- JEECG 單點登入 SSO
- 初探單點登入 SSO
- SSO單點登入邏輯
- 什麼是單點登入(SSO)
- CAS單點登入(SSO)實戰(一)
- CAS SSO單點登入框架學習框架
- Casdoor + OAuth 實現單點登入 SSOOAuth
- OAuth2實現單點登入SSOOAuth
- 14、sso單點登陸
- 記一次 SSO 單點登入實現
- 單點登入SSO和Oauth2.0 文章3OAuth
- 談談SSO單點登入的設計實現
- CAS實現單點登入SSO執行原理探究
- CAS SSO單點登入服務端環境搭建服務端
- CAS SSO單點登入客戶端環境搭建客戶端
- 實戰模擬│單點登入 SSO 的實現
- Spring+ Spring cloud + SSO單點登入應用認證SpringCloud
- SSO單點登入最全詳解(圖文全面總結)
- 整合spring cloud雲架構 - SSO單點登入之OAuth2.0登入認證SpringCloud架構OAuth
- Spring Cloud雲架構 - SSO單點登入之OAuth2.0登入認證(1)SpringCloud架構OAuth
- spring cloud微服務分散式雲架構-單點登入(SSO)SpringCloud微服務分散式架構
- 不務正業的前端之SSO(單點登入)實踐前端
- 3.CAS SSO單點登入客戶端環境搭建客戶端
- 推薦一個分散式單點登入框架XXL-SSO!分散式框架
- 整合spring cloud雲架構 - SSO單點登入之OAuth2.0登入認證(1)SpringCloud架構OAuth
- 最強SSO單點登入教程(三)單點登出流程分析
- 完全跨域的單點登入(SSO)解決方案原始碼解析跨域原始碼
- 對SSO單點登入和OAuth2.0的區別和理解OAuth
- Java架構-spring+springmvc+Interceptor+jwt+redis實現sso單點登入Java架構SpringMVCJWTRedis
- CAS實現單點登入SSO執行原理探究(終於明白了)
- 一箇中介軟體加幾行程式碼搞定 SSO 單點登入行程
- 如何自己實現一個健壯的 SSO 單點登入系統
- CAS SSO單點登入服務端環境搭建之框架深度分析服務端框架
- CAS SSO單點登入客戶端環境搭建之框架深度分析客戶端框架
- 2.CAS SSO單點登入服務端環境搭建原始碼服務端原始碼
- 單點登入