SSO单点登录方案大全

SSO单点登录方案大全分布式微服务系统主流常用的登录方案前言 单点登录其实是一个概念 主要是为了解决一次登录 多系统 本系统或外部系统 之间不需要重复登录的问题 就目前来说 主流的解决方案针对业务场景分为 3 个方向 1 同一公司 同父域下的单点登录解决方案 如 http map baidu com http www baidu com http image baidu com 基于 cooki

分布式微服务系统主流常用的登录方案

前言: 单点登录其实是一个概念,主要是为了解决一次登录,多系统(本系统或外部系统)之间不需要重复登录的问题,就目前来说,主流的解决方案针对业务场景分为3个方向:

1: 同一公司,同父域下的单点登录解决方案.

如[http://map.baidu.com] [http://www.baidu.com] [http://image.baidu.com]

基于cookie开源项目代表: JWT(https://jwt.io/);会详细介绍和实现;

2: 同一公司,不同域下的单点登录解决方案.

如[http://www.taobao.com] [http://www.tmall.com]

基于中央认证服务器开源项目代表:CAS(https://github.com/apereo/cas); https://www.apereo.org/projects/cas

3: 不同公司之间,不同域下的 第三方登录功能实现.

如第三方网站支持登录,微信登录,微博登录等;

基于OAuth2.0协议各大公司自己的支持;

一: 基于JWT的单点登录解决方案:

首先我们看一下图形,这个就是基于JWT解决方案的单点登录解决方案;

SSO单点登录方案大全 SSO单点登录方案大全

应用和原理 : 什么是JWT,什么业务场景使用JWT?

JWT:

//http://jwt.io/>

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
JSONWeb令牌是一种开放的、行业标准的RFC7519方法,用于在双方之间安全地表示声明。


JWT 应用场景:

Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用,JWT 省去了服务器存储用户信息的过程。

Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JWT的令牌结构 看官方文档https://jwt.io/introduction/ 并解释

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:Header,Payload,Signature。

官网对于这个三部分的定义。一个典型的JWT Token是 tttttttt.yyyyyyyyyyy.eeeeeee 这样组成。

SSO单点登录方案大全 SSO单点登录方案大全

Header : 由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。然后,用Base64对这个JSON编码就得到JWT的第一部分。

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。分为三种类型

1:registered claims。标准声明。一般常用于校验的有 iat,exp,nbf 校验 token 是否过期。

/*
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
*/







2:public claims。公开声明。 可以存放任意信息,比如userid等。不是信息安全的。

3:private claims。私密声明。 双方约定的信息。

Signature Base64(header).Base64(payload) + head中定义的算法 + 密钥 生成一个字符串 。

使用方式:

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

// Authorization: Bearer

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

通过代码实现JWT之单点登录(基于springboot+dubbo+zookeeper)

代码实现JWT服务器(基于dubbo实现):

JWTService:

package com.qianfeng.user;

import com.qianfeng.user.dto.UserRequest;
import com.qianfeng.user.dto.UserResponse;

/
* 用户服务对外接口
*
* @author Martin
*
*/
public interface IUserService {

/
* 用户登录接口
* @param request user info
* @return 查询到的结果
*/
UserResponse login(UserRequest request);

}





















JWT ServiceImpl:

JWT Util:

package com.qianfeng.user.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Map;

/
* JWT 生成token和提取token信息工具类
* @author Martin
*/
public class JWTTokenUtil {

static SignatureAlgorithm sa = SignatureAlgorithm.HS256;//使用hs256算法

//获取key
private static Key generatorKey(){

byte[] bin=DatatypeConverter.parseBase64Binary(“2521afcf18c749c1a8a7615c03d15e43”);
Key key=new SecretKeySpec(bin,sa.getJcaName());
return key;
}

/
* 将传过来的信息按照 Header,Payload,Signature 的方式组装一个字符串
* @param payLoad
* @return
*/
public static String generatorToken(Map

payLoad){


ObjectMapper objectMapper= new ObjectMapper();

try {

return Jwts.builder().setPayload(objectMapper.writeValueAsString(payLoad))
.signWith(sa,generatorKey()).compact();

} catch (JsonProcessingException e) {

e.printStackTrace();
}
return null;
}

/
* 将token解析,得到Payload内容
* @param token
* @return
*/
public static Claims phaseToken(String token){

//将token解析成claims
Jws

jws = Jwts.parser().setSigningKey(generatorKey()).parseClaimsJws(token);

// jws.getHeader(); Header
// jws.getBody(); Payload
// jws.getSignature(); Signature

return jws.getBody();
}

}





































































代码实现JWT客户端请求:

tocken校验拦截器:

package com.qianfeng.conf.interceptor;

import com.qianfeng.user.anno.Evade;
import com.qianfeng.user.util.CookieUtil;
import com.qianfeng.user.util.JWTTokenUtil;
import com.qianfeng.user.web.BaseController;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/
* 拦截器,如果没有token,直接返回结果。
* @author Martin
*/
public class TokenInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{

if(handler instanceof ResourceHttpRequestHandler || handler instanceof AbstractErrorController){

return true;
}

System.out.println(“start”);

HandlerMethod handlerMethod = (HandlerMethod)handler;

//如果需要规避
if(hasEvade(handlerMethod)){

System.out.println(“evade : ” +handlerMethod.toString());
return true;
}

//从cookie中获取token
String access_token = CookieUtil.getCookieValue(request, “access_token”);

//如果没有token,跳转到登录页面,这里需要判断一下是否是ajax请求
if(StringUtils.isEmpty(access_token)){

System.out.println(“token is null”);
return vafail(request,response);
}

Claims claims = null;
try{

claims = JWTTokenUtil.phaseToken(access_token); //解析token
}catch (ExpiredJwtException e){ //token已经过期
System.out.println(“token out time”);
return vafail(request,response);
}catch (SignatureException e1){ //签名校验失败
System.out.println(“token signature fail”);
return vafail(request,response);
}

if(null != claims && !StringUtils.isEmpty(claims.get(“uid”).toString())){

System.out.println(“token success”);

//获取id,并将id设置到Controller

if(handlerMethod.getBean() instanceof BaseController){

BaseController bean = (BaseController)handlerMethod.getBean();
bean.setUserID(claims.get(“uid”).toString());
}
return true;
}

System.out.println(“end”);
return false;
}

private boolean vafail(HttpServletRequest request,HttpServletResponse response) throws Exception{

if(CookieUtil.isAjax(request)){

response.getWriter().println(“{‘result’:’6666′,’msg’:’no token’}”);
return false;
}
response.sendRedirect(“login”);
return false;
}

/
* 验证是否可以规避,不需要拦截
* @param handlerMethod
* @return
*/
private boolean hasEvade(HandlerMethod handlerMethod){

Method method = handlerMethod.getMethod();

return method.getAnnotation(Evade.class) != null; //判断方法上面是否有自定义的规避注解。

}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

// TODO
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

// TODO
}
}
















































































































LoginController登录实现代码:

package com.qianfeng.user.web;

import com.alibaba.dubbo.config.annotation.Reference;
import com.qianfeng.user.anno.Evade;
import com.qianfeng.user.dto.UserRequest;
import com.qianfeng.user.dto.UserResponse;
import com.qianfeng.user.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/
* 登录
* @author Martin
*/
@Controller
public class LoginController extends BaseController{

@Reference //通过dubbo注入实现类
IUserService userService;

@GetMapping(“/login”)
@Evade //自定义注解,配置该注解不拦截请求;
public String jumpLogin(){

return “login”;
}

@RequestMapping(value = “/dologin”)
@Evade
public String doLogin(UserRequest ur, HttpServletRequest request, HttpServletResponse response){

UserRequest res = new UserRequest(ur.getUserName(),ur.getUserPass());

if(null == userService){ //dubbo服务不可用
return “error”;
}

UserResponse rp = userService.login(res); //通过dubbo调用用户服务进行登录

//登录失败跳转到登录页面
if(null == response || StringUtils.isEmpty(rp.getUid())){

return “login”;
}

//按照以前,如果登录成功,是需要将用户信息保存到session中。
request.getSession().setAttribute(“user”,rp);
request.setAttribute(“uid”,rp.getUid());

//使用token的方式,将信息保存到客户端 access_token : 注意,这里在获取的时候也需要这个值
//response.addHeader(“Set-Cookie”,”access_token=”+rp.getToken()+”;Path=/;HttpOnly”); //根目录的 cookie 设置token

return “index”;
}

//用来测试,是否可以直接访问资源
@RequestMapping(value = “/buy”)
public String buy(HttpServletRequest request){

//从Session中获取用户信息
UserResponse ur = (UserResponse)request.getSession().getAttribute(“user”);
System.out.println(“Session中: /buy : id ” + ur.getUid());

// System.out.println(“Token中:/buy : id ” + getUserID());
// request.setAttribute(“uid”,getUserID());

return “info”;
}

}










































































Evade 自定义注解配置不需要验证的请求:

package com.qianfeng.user.anno;

import java.lang.annotation.*;

/
* 自定义注解,该注解可以设置不拦截处理
* @author Martin
*/
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Evade {

}













以上为主要实现代码

二: 基于CAS同公司不同域的单点登录解决方案

Yelu大学研发的CAS单点登录原理:

SSO单点登录方案大全 SSO单点登录方案大全

主要步骤分为:

1:用户请求CAS Client的某一个网站,或者直接请求CAS Server登录页面;

2:用户到CAS Server的登录页面进行验证,验证成功,CAS Server返回ticket到客户端;

3:客户端再次请求CAS Client资源, CAS Client携带ticket到CAS Server认证,成功则继续访问;

实现步骤:

1: 搭建CAS Server端,可使用CAS的框架实现;可自定义登录页面;实现其内部接口即可;

2: 搭建CAS Client端,就是公司不同的服务器系统,携带秘钥即可;

三: 基于Auth2.0之第三方登录

场景: 公司的网站支持第三方登录(登录,微信登录,微博登录):

开放平台:https://connect..com/index.html

微博开放平台: https://open.weibo.com/authentication/

接入方式按照对应的平台操作即可,第三方网站接入文档提示非常全面.

关于登录,还请题主需要关注一下 分布式下登录成功以后,分布式Session的问题以及解决方案;

方案是用来解决问题的,这些方案没有好不好的说法,只有合不合适的说法,合适的才是最好的。分布式微服务系统主流常用的登录方案

前言: 单点登录其实是一个概念,主要是为了解决一次登录,多系统(本系统或外部系统)之间不需要重复登录的问题,就目前来说,主流的解决方案针对业务场景分为3个方向:

1: 同一公司,同父域下的单点登录解决方案.

如[http://map.baidu.com] [http://www.baidu.com] [http://image.baidu.com]

基于cookie开源项目代表: JWT(https://jwt.io/);会详细介绍和实现;

2: 同一公司,不同域下的单点登录解决方案.

如[http://www.taobao.com] [http://www.tmall.com]

基于中央认证服务器开源项目代表:CAS(https://github.com/apereo/cas); https://www.apereo.org/projects/cas

3: 不同公司之间,不同域下的 第三方登录功能实现.

如第三方网站支持登录,微信登录,微博登录等;

基于OAuth2.0协议各大公司自己的支持;

一: 基于JWT的单点登录解决方案:

首先我们看一下图形,这个就是基于JWT解决方案的单点登录解决方案;

SSO单点登录方案大全 SSO单点登录方案大全

应用和原理 : 什么是JWT,什么业务场景使用JWT?

JWT:

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
JSONWeb令牌是一种开放的、行业标准的RFC7519方法,用于在双方之间安全地表示声明。

JWT 应用场景:

Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用,JWT 省去了服务器存储用户信息的过程。

Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JWT的令牌结构 看官方文档https://jwt.io/introduction/ 并解释

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:Header,Payload,Signature。

官网对于这个三部分的定义。一个典型的JWT Token是 tttttttt.yyyyyyyyyyy.eeeeeee 这样组成。

SSO单点登录方案大全 SSO单点登录方案大全

Header : 由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。然后,用Base64对这个JSON编码就得到JWT的第一部分。

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。分为三种类型

1:registered claims。标准声明。一般常用于校验的有 iat,exp,nbf 校验 token 是否过期。

2:public claims。公开声明。 可以存放任意信息,比如userid等。不是信息安全的。

3:private claims。私密声明。 双方约定的信息。

Signature Base64(header).Base64(payload) + head中定义的算法 + 密钥 生成一个字符串 。

使用方式:

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

// Authorization: Bearer

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

通过代码实现JWT之单点登录(基于springboot+dubbo+zookeeper)

代码实现JWT服务器(基于dubbo实现):

JWTService:

JWT ServiceImpl:

JWT Util:

代码实现JWT客户端请求:

tocken校验拦截器:

LoginController登录实现代码:

Evade 自定义注解配置不需要验证的请求:

以上为主要实现代码,如需全部工程代码请加 :

二: 基于CAS同公司不同域的单点登录解决方案

Yelu大学研发的CAS单点登录原理:

SSO单点登录方案大全 SSO单点登录方案大全

主要步骤分为:

1:用户请求CAS Client的某一个网站,或者直接请求CAS Server登录页面;

2:用户到CAS Server的登录页面进行验证,验证成功,CAS Server返回ticket到客户端;

3:客户端再次请求CAS Client资源, CAS Client携带ticket到CAS Server认证,成功则继续访问;

实现步骤:

1: 搭建CAS Server端,可使用CAS的框架实现;可自定义登录页面;实现其内部接口即可;

2: 搭建CAS Client端,就是公司不同的服务器系统,携带秘钥即可;

三: 基于Auth2.0之第三方登录

场景: 公司的网站支持第三方登录(登录,微信登录,微博登录):

开放平台:https://connect..com/index.html

微博开放平台: https://open.weibo.com/authentication/

接入方式按照对应的平台操作即可,第三方网站接入文档提示非常全面.

关于登录,还请题主需要关注一下 分布式下登录成功以后,分布式Session的问题以及解决方案;

方案是用来解决问题的,这些方案没有好不好的说法,只有合不合适的说法,合适的才是最好的。

转自https://www.zhihu.com/question//answer/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228405.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午7:06
下一篇 2026年3月16日 下午7:06


相关推荐

  • PotPlayer最新 中文,绿色版 PotPlayer

    PotPlayer最新 中文,绿色版 PotPlayer今天推荐一款纯净、无广告、极速、功能强大的播放器:PotplayerPotPlayer到底好在哪里?【知乎】PotPlayer有独特的高级功能!第一,支持单帧播放(F键前进一帧,D键倒退一帧,空格键正常播放)第二,支持变速播放(X键降速10%,C键提速10%,Z键重置)第三,支持5.1声道和7.1声道,可以搭建环绕声家庭影院第四,支持画声同步调节(<键画面提前50ms,>键画面延后50ms,?键重置)第五,按Tab键就能显示视频文件的详细信息第六,支持硬解码,支持软…

    2022年7月14日
    18
  • 计算机的历程_计算机的发展史简介课件

    计算机的历程_计算机的发展史简介课件一、史前时代:1623——1895

    2022年10月19日
    5
  • 泛函分析笔记3:内积空间

    泛函分析笔记3:内积空间我们前面讲了距离空间 赋范空间 距离空间赋予了两个点之间的距离度量 范数赋予了每个点自身的长度度量 而范数则可以导出距离 本章要讲的内积可以看成是更加统一的定义 因为从内积我们可以导出范数 进而导出距离 因此内积空间是一个 更小 的空间 在此基础上结合完备性我们引出了 Hilbert 空间 后续我们的研究也大多集中在 Hilbert 空间上 文章目录 1 内积空间 2 正交补与正交投影 3 标准正交基 4 Hilbert 空间上有界线性泛函表示 1 内积空间定义 XXX 为 K mathbb K K

    2026年3月26日
    2
  • 百度Echarts-免费的商业产品图表库

    百度Echarts-免费的商业产品图表库

    2021年9月5日
    44
  • idle python和pycharm_请问大佬们新手用pycharm还是idle啊?

    idle python和pycharm_请问大佬们新手用pycharm还是idle啊?既然你要重复地问 我就重复地答 以下答案拷自 python 新手入门使用自带的 IDLE 用 pycharm 还是 visualstudio www zhihu com 不建议新手用 IDLE 也不建议用任何命令行 Shell 方式 这些其实是高手用的 我甚至不建议你安装 Python 我也不建议你安装任何 IDE 无论是 Pycharm 还是 VSC 统统不建议 为什

    2026年3月17日
    2
  • Lucene.net(4.8.0) 学习问题记录二: 分词器Analyzer中的TokenStream和AttributeSource[通俗易懂]

    Lucene.net(4.8.0) 学习问题记录二: 分词器Analyzer中的TokenStream和AttributeSource[通俗易懂]前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移。因为项目整体要迁移到ASP.NETCore2.0版本,而Lucene使用的版本是3.6.0,PanGu分词也是对应Lucene3.6.0版本的。不过好在Lucene.net已经有了Core2.0版本,4.8.0bate版,而PanGu分词,目前有人正在做,貌似已经做完,只是…

    2022年7月22日
    17

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号