日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

SSO 轻量级实现指南(原生 Java 实现):SSO Client 部分

發(fā)布時間:2023/12/15 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SSO 轻量级实现指南(原生 Java 实现):SSO Client 部分 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

根據(jù)單點登錄的定義,客戶端可以完全不用創(chuàng)建自己的用戶系統(tǒng),它只需要接入 SSO 中心的服務就好。SSO 中心關(guān)于用戶的常規(guī)業(yè)務都在其內(nèi)。那么客戶端接入單點登錄,需要做什么工作呢?首先用戶一般常規(guī)操作有:

  • 用戶注冊。這部分 SSO 中心提供注冊接口。客戶端自定義自己風格注冊 UI,跨域請求數(shù)據(jù)到 SSO 中心接口即可;
  • 用戶登錄。這部分 SSO 中心提供登錄接口。客戶端自定義自己風格登錄 UI,跨域請求數(shù)據(jù)到 SSO 中心接口即可;
  • 用戶注銷登陸。這部分 SSO 中心提供登錄接口,跨域請求數(shù)據(jù)到 SSO 中心接口即可;
  • 用戶常規(guī)查詢操作,例如查詢列表、單個用戶詳情等,這部分 SSO 中心開放相關(guān) API。

一般常規(guī)接口上文已經(jīng)討論過了。可見 SSO 中心一個特性要求便是允許“跨域訪問”,這個問題不大,進行相關(guān)配置即可。

SSO 中心,即認證中心,關(guān)鍵一點在于用戶的認證。除了上述登錄是重要的認證過程外,每次涉及相關(guān)操作都必須進行認證,否則就是非法訪問。

認證的問題

如果按照 OAuth 本來的目的,資源服務器跟認證服務器是在一塊的,比如說微博,它有個開放平臺你可以根據(jù) AccessToken 獲取它微博內(nèi)容。每次訪問都有提供 AccessToken 參數(shù),看是否合法才允許訪問。

但目前我們搞的不是純粹 OAuth,上文《SSO 與 OAuth 傻傻分不清?》小節(jié)已經(jīng)說過了。SSO 認證中心往往不是跟資源服務器在一起的“單體”結(jié)構(gòu),而是獨立部署的;而且應用端(即客戶端)肯定都有自己的資源服務,肯定需要用戶認證、權(quán)限校驗之類的操作。那么問題來了,校驗客戶端憑證令牌(即 AccessToken)這項工作,——是放在應用端還是 SSO 中心呢?

顯然易見,作為統(tǒng)一的認證中心,SSO 中心無疑擁有最根本的用戶狀態(tài)記錄,一切皆以 SSO 中心的為準。但每次訪問資源的認證工作都要通訊 SSO 中心,性能成本會不會太高呢?對于 SSO 中心服務器的性能也是嚴重的考驗。對此,筆者考慮了以下幾個個解決方案。

  • 還是在 SSO 中心校驗,但采取優(yōu)化手段:對已驗證的 token 進行緩存,僅首次訪問時調(diào)用 SSO 驗證一次,一般緩存10分鐘這種,便于 SSO 進行 token 撤銷。
  • 無須 SSO 校驗 token,采用自描述的 token。這種自描述的 Token 比普通的 Token 的復雜,解密之后包含了更多的信息,根據(jù)這些信息對比、校驗便能清楚是否合法,以及一定的用戶信息。舉個例子,如“重置密碼”,在郵件中包含一個帶 token 的連接,后端得到這 token 后其實有時間戳的信息的,再對比一下便能知道是否超時的請求。
  • 采用自描述的 Token,其實跟大家說 JWT 就可以了,它就是干這事的。不過筆者說實話還不太懂 JWT,當前方案中還沒有使用 JWT。
  • 應用端自建用戶登錄會話。其實就是冗余一套 SSO 中心的,用戶登錄之后回來馬上搞自己的 Session。但怎么同步是個問題,而且隱約好像不是“單點”的意思了。當前我正在使用這方案。

應用端自建用戶登錄會話

既然選定了這個方案,那我們就看看怎么做吧。首先是用戶登錄之后馬上建立 Session。源碼在這里。

這屬于客戶端登錄的一部分,得到授權(quán)碼之后在服務端發(fā)起請求。

@GetMapping(value = "clientLogin", produces = JSON) public String clientLogin(@RequestParam String code, HttpServletRequest req) {Map<String, Object> params = new HashMap<>();params.put("code", code);params.put("grant_type", GRANT_TYPE);params.put("client_id", clientId);params.put("client_secret", clientSecret);Map<String, Object> result = Post.api(api + "/sso/authorize", params);UserSession saveSession = saveSession(result);// 存入 sessionreq.getSession().setAttribute(saveSession.accessToken.getAccessToken(), saveSession);return "${User.home}".equals(userHome) ? toJson(result) : "redirect:/" + userHome; }/*** JSON 結(jié)果轉(zhuǎn)換為 Session 存儲,形成本地登錄狀態(tài)* * @param result* @return*/ static UserSession saveSession(Map<String, Object> result) {AccessToken accessToken = new AccessToken();accessToken.setAccessToken(result.get("access_token").toString());accessToken.setRefreshToken(result.get("refresh_token").toString());accessToken.setScope(result.get("scope").toString());accessToken.setExpiresIn(((Integer) result.get("expires_in")).longValue());@SuppressWarnings("unchecked")Map<String, Object> userJson = (Map<String, Object>) result.get("user");User user = MapTool.map2Bean(userJson, User.class, true);UserSession userSession = new UserSession();userSession.accessToken = accessToken;userSession.user = user;return userSession; }

若登錄成功,就在客戶端本地產(chǎn)生 Session。其中重點就是 UserSession ,它包含了用戶和 AccessToken 兩種對象,以 Token 為 key 存到 Session 中。

校驗攔截 Token

有了本地的用戶登錄狀態(tài),就無須訪問 SSO 中心校驗了,于是也變得簡單和高效了。所有校驗都發(fā)生在本地進行。我們看看這個攔截器 SsoAccessTokenInterceptor,它是標準的 Spring 攔截器。

你先需要在 yaml 配置中定義一下要保護資源的訪問路徑,即接口,按照 Spring 攔截器的配置。

User:resources: /api/**, /user/** # 要保護的資源excludeResources: /user/login/** # 排除的路徑

記得路徑后面要加上 ** 同貝所有子路徑。

/*** 要保護的資源(只有登錄了才能訪問)*/ @Value("${User.resources}") private String[] protectPerfix;/*** 要保護的資源(只有登錄了才能訪問)*/ @Value("${User.excludeResources}") private String[] excludeResources;/*** 加入攔截器*/ @Override public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(tokenInterceptor).addPathPatterns(protectPerfix).excludePathPatterns(excludeResources);super.addInterceptors(registry); }

攔截器代碼

import java.io.IOException; import java.time.LocalDateTime;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor;import com.ajaxjs.framework.BaseController; import com.ajaxjs.user.sso.model.AccessToken; import com.ajaxjs.user.sso.model.UserSession; import com.ajaxjs.util.date.LocalDateUtils;/*** 校驗 AccessToken 的攔截器* * @author Frank Cheung<sp42@qq.com>**/ @Component public class SsoAccessTokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {String accessToken = req.getParameter("access_token");if (!StringUtils.hasText(accessToken)) {err(resp, "缺少 access_token 參數(shù)");return false;}Object object = req.getSession().getAttribute(accessToken);if (object == null) {// TODO 是否拿 Token 去 SSO 中心再校驗一下err(resp, "非法 AccessToken");return false;} else {}UserSession userSess = (UserSession) object;// 如果 Access Token 已經(jīng)失效,則返回錯誤提示if (checkIfExpire(userSess.accessToken)) {// TODO 是否要刪除過期 token?err(resp, "access_token 已超時");return false;} elsereturn true;}/*** 獲取 expiresIn 與當前時間對比,看是否超時* * @param token 令牌* @return true 表示超時*/static boolean checkIfExpire(AccessToken token) {long expiresIn = token.getExpiresIn();LocalDateTime expiresDateTime = LocalDateUtils.ofEpochSecond(expiresIn);// 過期日期return expiresDateTime.isBefore(LocalDateTime.now());}static void err(HttpServletResponse resp, String msg) {resp.setStatus(HttpStatus.UNAUTHORIZED.value());resp.setHeader("Content-type", "application/json;charset=UTF-8");try {resp.getWriter().write(BaseController.jsonNoOk(msg));} catch (IOException e) {e.printStackTrace();}} }

SSO Client

上面所述的所有代碼都在 SSO Client 這個工程中,可以通過 Maven 加入到你的工程中。

設(shè)置 Session 超時時間,在 web.xml 配置一下。

<!-- 時間單位為分鐘 --> <session-config><session-timeout>15</session-timeout> </session-config>

Spring Boot 設(shè)置 yml

server:port: 8089session:timeout: 1800 #以秒為單位

Ja
va 設(shè)置:

session.setMaxInactiveInterval(30*60;//以秒為單位

總結(jié)

以上是生活随笔為你收集整理的SSO 轻量级实现指南(原生 Java 实现):SSO Client 部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。