基於Oauth2.0實現SSO單點認證
什麼是單點?
借用百度百科的話:
單點登入(Single Sign On),簡稱為 SSO,是比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統。
單點登入圖解:
什麼是Oauth2.0?
OAuth 就是一種授權機制。資料的所有者告訴系統,同意授權第三方應用進入系統,獲取這些資料。系統從而產生一個短期的進入令牌(token),用來代替密碼,供第三方應用使用。
引用阮一峰老師的文章:OAuth 2.0 的四種方式 - 阮一峰的網路日誌
正文:
前段時間在開發自己部落格的時候,需要用到登入,當時的第一想法是做簡單的密碼驗證就行了。不過為了熟悉一下SSO的流程,還是準備試用一下。
讓我從SSO轉變為Oauth的地方,是因為:SSO的限制,SSO需要在同一主域名下才能有效地寫入cookie例如 www.baidu.com,如果試用SSO ,SSO的域名就需要是 ***.baidu.com。當然,不排除有其他方法可以實現cookie的寫入。但正常的SSO流程就是需要在同一主域名下。
而Oauth2.0並沒有限制,而且Oauth2.0更符合我對單點登入的認知。
開發流程:
1:授權系統開發
授權系統,這裡其實就當做是一個最簡單的登入驗證器使用就行,有一個基礎的增刪改查就行
package com.hao.sso.vali.controller;
import java.util.concurrent.TimeoutException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.hao.bios.core.cache.ZboxCacheUtil;
import com.hao.bios.core.utils.StringUtil;
import com.hao.bios.core.utils.ZboxResult;
import com.hao.sso.vali.SsoConstants;
import com.hao.sso.vali.service.SsoLoginService;
import com.hao.sso.vali.vo.SsoLoginValiVO;
import com.hao.sso.vali.vo.ZboxSsoAuthPassVO;
import net.rubyeye.xmemcached.exception.MemcachedException;
@Controller
@RequestMapping("/sso/login")
public class LoginController {
@Autowired
private ZboxCacheUtil cache;
@Autowired
private SsoLoginService ssoLoginService;
@RequestMapping("/isLogin")
@ResponseBody
public boolean isLogin(HttpServletRequest req, String accessToken) {
if (StringUtil.isEmpty(accessToken)) {
return false;
}
ZboxSsoAuthPassVO authVO = cache.get(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, ZboxSsoAuthPassVO.class);
if (authVO == null) {
return false;
}
long outTime = authVO.getExpires();
long nowTime = System.currentTimeMillis();
if (outTime!=0 && nowTime > outTime) {
return false;
}
return true;
}
@RequestMapping("/login")
@ResponseBody
public String login(ServletRequest request, ServletResponse response, String userName, String pwd) throws TimeoutException, InterruptedException, MemcachedException {
HttpServletRequest req = (HttpServletRequest) request;
boolean isLogin = ssoLoginService.checkLogin(userName, pwd);
if (isLogin) {
SsoLoginValiVO valiVO = new SsoLoginValiVO();
String ip = getIpAddress(req);
valiVO.setIp(ip);
valiVO.setIsLogin(true);
valiVO.setLoginTime(StringUtil.getNowTimeString());
valiVO.setOutTime(0);
valiVO.setUserId(userName);
String key = StringUtil.getUUID();
cache.put(SsoConstants.LOGIN_VALI_CACHE_POOL, key, valiVO);
String sign = StringUtil.getUUID();
cache.put(SsoConstants.LOGIN_AUTH_CACHE_POOL, sign, key);
return sign;
}
return "";
}
@RequestMapping("/getAuthPass")
@ResponseBody
public ZboxResult getAuthPass(ServletRequest request, ServletResponse response, String sign) throws TimeoutException, InterruptedException, MemcachedException {
ZboxResult rul = new ZboxResult();
String key = cache.get(SsoConstants.LOGIN_AUTH_CACHE_POOL, sign, String.class);
if (StringUtil.isEmpty(key)) {
rul.setSuccess(false);
rul.setMessage("授權碼無效");
return rul;
}
SsoLoginValiVO vali = cache.get(SsoConstants.LOGIN_VALI_CACHE_POOL, key, SsoLoginValiVO.class);
if (vali == null) {
rul.setSuccess(false);
rul.setMessage("未登入");
return rul;
}
if (vali.getIsLogin() == false) {
rul.setSuccess(false);
rul.setMessage("登陸失效");
return rul;
}
long outTime = vali.getOutTime();
long nowTime = System.currentTimeMillis();
if (outTime!=0 && nowTime > outTime) {
rul.setSuccess(false);
rul.setMessage("登陸超時");
return rul;
}
String accessToken = StringUtil.getUUID();
long expires = System.currentTimeMillis() + 60*60*1000L;
String refreshToken = StringUtil.getUUID();
//獲取使用者資訊
String info = "";
ZboxSsoAuthPassVO authVO= new ZboxSsoAuthPassVO();
authVO.setAccessToken(accessToken);
authVO.setExpires(expires);
authVO.setInfo(info);
authVO.setRefreshToken(refreshToken);
authVO.setScope("all");
authVO.setTokenType("zbox_user_pass");
authVO.setUid(vali.getUserId());
cache.put(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, authVO);
rul.setSuccess(true);
rul.setData(authVO);
return rul;
}
@RequestMapping("/loginOut")
@ResponseBody
public boolean loginOut(ServletRequest request, String accessToken) throws TimeoutException, InterruptedException, MemcachedException {
ZboxSsoAuthPassVO authVO = cache.get(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, ZboxSsoAuthPassVO.class);
if (authVO != null) {
cache.put(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, null);
cache.delete(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken);
}else {
return false;
}
return true;
}
public String getSsoCookie(HttpServletRequest req) {
Cookie[] cookies = req.getCookies();
if (cookies!=null) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
if (SsoConstants.SSO_LOGIN_VALI_COOKIE_KEY.equals(name)) {
return cookie.getValue();
}
}
}
return "";
}
public String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("X-Forward-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("x-forwarded-for");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
具體其實就是幾個介面:登入、登出、驗證登入、授權令牌
2:業務系統接入
直接上邏輯流程:
1:使用者訪問業務系統
2:業務系統先驗證本地cookie是否有效,
無效:直接返回前臺未登入;
有效:訪問認證系統,檢查登入情況,未登入則返回業務系統,業務系統返回前臺未登入
3:業務檢查到未登入,跳轉到認證系統登入頁面進行登入,登入成功之後認證系統返回一個授權碼
4:業務系統攜帶授權碼,在後端使用雙方協議的id和祕鑰(授權系統給業務系統的一個賬戶密碼)攜帶著授權碼,去認證系統獲取令牌。
5:業務系統拿到令牌後使用令牌訪問認證系統的api。
相關文章
- 整合spring cloud雲架構 - SSO單點登入之OAuth2.0登入認證SpringCloud架構OAuth
- Spring Cloud雲架構 - SSO單點登入之OAuth2.0登入認證(1)SpringCloud架構OAuth
- 整合spring cloud雲架構 - SSO單點登入之OAuth2.0登入認證(1)SpringCloud架構OAuth
- oauth2.0實現sso單點登入的方式和相關程式碼OAuth
- 單點登入SSO和Oauth2.0 文章3OAuth
- 在Spring Boot中實現OAuth2.0認證Spring BootOAuth
- Spring+ Spring cloud + SSO單點登入應用認證SpringCloud
- 基於IdentityServer4的OIDC實現單點登入(SSO)原理簡析IDEServer
- Spring Security——基於表單登入認證原理及實現Spring
- Casdoor + OAuth 實現單點登入 SSOOAuth
- OAuth2實現單點登入SSOOAuth
- JAVA 多使用者商城系統b2b2c-SSO單點登入之OAuth2.0登入認證(1)JavaOAuth
- OAuth2.0協議入門(三):OAuth2.0授權與單點登入(SSO)的區別以及單點登入服務端從設計到實現OAuth協議服務端
- OAuth2.0實戰!使用JWT令牌認證!OAuthJWT
- 對SSO單點登入和OAuth2.0的區別和理解OAuth
- 實戰模擬│單點登入 SSO 的實現
- java B2B2C Springboot電子商務平臺原始碼-SSO單點登入之OAuth2.0登入認證JavaSpring Boot原始碼OAuth
- 基於 go-zero 輕鬆實現 JWT 認證GoJWT
- egg基於jsonwebtoken的Token實現認證機制JSONWeb
- 基於JWT規範實現的認證微服務JWT微服務
- CAS實現單點登入SSO執行原理探究(終於明白了)
- ASP.NET Core Authentication系列(四)基於Cookie實現多應用間單點登入(SSO)ASP.NETCookie
- 記一次 SSO 單點登入實現
- 基於使用者認證的前後端實現後端
- 談談SSO單點登入的設計實現
- CAS實現單點登入SSO執行原理探究
- (十)JavaspringcloudB2B2Co2o多使用者商城springcloud架構-SSO單點登入之OAuth2.0登入認證(1)JavaSpringGCCloudDB2架構OAuth
- 基於 SASL/SCRAM 讓 Kafka 實現動態授權認證Kafka
- 基於JWT標準的使用者認證介面實現JWT
- 如何基於 Security 實現 OIDC 單點登入?
- 如何基於Security實現OIDC單點登入?
- OAuth2.0認證授權workflow探究OAuth
- CAS單點登入(SSO)實戰(一)
- 基於Spring Security Oauth2的SSO單點登入+JWT許可權控制實踐SpringOAuthJWT
- 單點登入(SSO)
- SSO 單點登入
- SSO單點登入
- (十) Java B2B2C o2o多使用者商城 springcloud架構 - SSO單點登入之OAuth2.0登入認證(1)JavaSpringGCCloud架構OAuth