Coder Social home page Coder Social logo

sso-client's Introduction

Maven Central License

品高单点登录客户端(Java)

品高单点登录遵循OAuth 2.0Open ID Connect Core协议,在继续阅读前请先了解一下相关的概念。

版本说明

版本 说明
3.0.4 兼容旧版本SSO(3.1.x)的scope使用,号分隔和新版本使用 分隔的情况,新版本SSO(3.2.0)返回的scope以空格分隔,Authentication.getScope()会全部替换成,分隔
3.1.0 对接新版本SSO(3.2.0),Authentication.getScope()返回的scope以 分隔
3.1.1 对接新版本SSO(3.2.0),SSOConfig.getDefaultReturnUrl()返回默认的登录后回调地址

安装

Maven

<dependency>
	<groupId>net.bingosoft.oss</groupId>
	<artifactId>sso-client</artifactId>
	<version>[3.0.1,]</version>
</dependency>

运行环境

SDK 版本 Java 版本
3.x.x 6+

外部依赖

名称 版本 依赖说明
fastjson 1.2.31+ JSON解析
slf4j 1.7.5+ 程序日志
commons-codec 1.10+ Base64解码

前提条件

在使用之前需要到单点登录服务中注册一个应用,申请以下参数:

参数 必须 说明
client_id 应用的标识
client_secret 应用的密钥
redirect_uri 应用登录后返回的地址,用到登录功能才需要注册

注:如何注册一个应用不在此文档中描述

使用

1. 配置SSOClient对象

配置SSOClient对象需要先构造一个SSOConfig对象,示例如下:

// 创建SSOConfig对象
SSOConfig config = new SSOConfig();
// 设置应用标识
config.setClientId("clientId");
// 设置应用密钥
config.setClientSecret("clientSecret");
// 设置应用资源名称,作为服务时,需要在SSO注册资源名称,没设置的情况下,默认使用clientId属性
config.setResourceName("resourceName");
// 设置回调地址
config.setRedirectUri(redirectUri);
// 根据SSO地址自动配置其他地址
config.autoConfigureUrls("http://sso.example.com");

// 3.0.5-SNAPSHOT版本开始,增加登录时设置注销地址功能
config.setLogoutUri("http://localhost:8080/logout");
// 3.1.1-SNAPSHOT版本开始支持,增加设置默认的回调地址
config.setDefaultReturnUrl("http://localhost:8080/return_url");

// 创建client对象
SSOClient client = new SSOClient(config);

2. 身份认证 (Authentication)

在Restful API中,对于遵循OAuth 2.0标准协议的请求,使用如下方式校验用户身份:

HttpServletRequest req;

// 获取access token
String accessToken = SSOUtils.extractAccessToken(req);

Authentication authc = null;
try{
    authc = client.verifyAccessToken(accessToken);            
}catch (InvalidTokenException e){
    // 处理access token无效的情况
}catch (TokenExpiredException e){
    // 处理access token过期的情况
}
// userId:用户ID,username:用户登录名(loginName),clientId:应用ID,scope:授权列表,expires:过期时间
String userId = authc.getUserId();
String username = authc.getUsername();
String client = authc.getClientId();
String scope = authc.getScope();
// 获取access token的过期时间,这个过期时间指的是距离标准日期1970-01-01T00:00:00Z UTC的秒数
long expires = authc.getExpires();

3. 登录注销 (Login & Logout)

对于普通的web应用,使用SDK按照如下方式接入品高SSO实现单点登录和单点注销。

3.1 登录

  1. 实现一个AbstractLoginServlet的HttpServlet
package demo;

//...

public class LoginServlet extends net.bingosoft.oss.ssoclient.servlet.AbstractLoginServlet {
    @Override
    protected SSOClient getClient(ServletConfig config) {
        // 返回一个配置好的SSOClient对象
        return new SSOClient();
    }
    @Override
    protected void localLogin(HttpServletRequest req, HttpServletResponse resp, Authentication authc,
                              AccessToken token) {
        // 省略本地登录代码...
    }
}
  1. web.xml中配置这个实现类的访问路径
<servlet>
    <servlet-name>ssologin</servlet-name>
    <servlet-class>demo.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ssologin</servlet-name>
    <!-- 这个servlet的访问地址,非必要的情况建议不要修改 -->
    <url-pattern>/ssoclient/login</url-pattern>
</servlet-mapping>
  1. 设置登录跳转地址

在web应用中,对所有需要登录的请求重定向到/ssoclient/login这个地址上进行单点登录。

注:可以通过设置return_url参数决定登录完成后跳转的地址,参数值是经过url编码的地址,如:
return_url=http%3A%2F%2Flocalhost%3A8080%2Fdemo

到这里接入配置完成。

3.2 注销

单点注销只要跳转到SSO注销地址注销即可。

// HttpServletResponse resp;
// SSOClient client;
// 注销后的返回地址
String returnUrl = "http://www.example.com";
String ssoLogoutUrl = SSOUtils.getSSOLogoutUrl(client,returnUrl);
resp.sendRedirect(ssoLogoutUrl);

4. 获取访问令牌 (Obtain Access Token)

访问令牌(access token)是用来代表请求发起者的身份的,一般来说访问令牌有两种可能:

  • 仅代表应用身份:用于调用只需要验证应用身份的服务
  • 同时代表用户和应用的身份:用于调用同时需要验证用户身份和应用身份的服务,也可以用于只校验应用的情况

访问令牌一般有三个属性:

AccessToken token = new AccessToken();

// 访问令牌,真正代表用户和应用身份的令牌
String accessToken = token.getAccessToken();

// 刷新令牌,当这个访问令牌过期后,可以用刷新令牌换取新的访问令牌
String refreshToken = token.getRefreshToken();

// 过期时间,指的是距离标准日期1970-01-01T00:00:00Z UTC的秒数
long expires = token.getExpires();

访问令牌的获取有如下几种方式。

4.1 通过授权码(Authorization Code)获取新的访问令牌

授权码的获取可以参考OpenId Connect CodeFlowSteps。 获取到授权码code后,可以利用code获取访问令牌:

AccessToken token = client.obtainAccessTokenByCode(code);

这个令牌代表的身份由授权码决定,一般是代表用户身份和生成这个code的应用身份。

注:这里使用client对象验证授权码生成访问令牌,因此访问令牌代表的是client的身份和用户身份。

4.2 通过应用凭证(client credentials)获取访问令牌

当我们的服务需要调用另一个服务的时候,如果被调用的服务需要并且只需要确认client身份,这个时候可以使用仅代表client身份的访问令牌:

AccessToken token = client.obtainAccessTokenByClientCredentials();

这里使用client自己的访问凭证获取访问令牌,这个令牌只能代表client自己的身份。

4.3 通过已有的访问令牌获取新的访问令牌

当我们的服务需要调用另一个服务,并且被调用的服务需要同时验证用户身份和client身份的时候,这个时候我们需要一个能代表用户身份和client身份的访问令牌。

在我们的服务接收请求的时候,已经获取到一个代表用户身份的访问令牌。

// HttpServletRequest req;
String accessToken = SSOUtils.extractAccessToken(req);
AccessToken clientAndUser = client.obtainAccessTokenByToken(accessToken);

这里accessToken代表的是用户身份,clientAndUser代表的是用户身份和client的身份。 使用clientAndUser这个访问令牌就可以调用另一个服务了。

5. 刷新访问令牌

获取访问令牌之后,可能需要观察访问令牌的过期时间。可以按照如下方式判断访问令牌是否过期:

AccessToken at;
// 省略at获取的过程。
if(at.isExpired()){
    // 访问令牌过期
}else {
    // 访问令牌未过期
}

如果访问令牌已经过期了,这个令牌就不能再用了,需要刷新访问令牌:

if(at.isExpired()){
    // 这里获取到的访问令牌是全新的访问令牌,因此需要重新赋值。
    at = client.refreshAccessToken(at);
}

扩展

自定义缓存

SDK中提供了简单的access token校验缓存实现,在实际应用中可以根据需求定制CacheProvider。

定制CacheProvider需要实现CacheProvider接口,并用实现类的对象覆盖默认的CacheProvider,示例如下:

class CustomCacheProvider implements net.bingosoft.oss.ssoclient.spi.CacheProvider{
    // 省略实现代码...
}

使用定制的CacheProvider对象

SSOClient client = new SSOClient(config);
client.setCacheProvider(new CustomCacheProvider());

常见问题

问:配置好单点登录后,在跳转到SSO时浏览器收到invalid_request:invalid redirect_uri错误。

答:这是由于注册应用时设置的回调地址(redirect_uri)不能匹配SDK生成的回调地址导致的,SDK生成的回调地址一般是如下格式:

http(s)://${domain}:${port}/${contextPath}/ssoclient/login?${queryString}
如:http://www.example.com:80/demo/ssoclient/login?name=admin

请自行查阅SSO应用注册相关文档,确认应用的回调地址能匹配SDK生成的回调地址,如:

http://www.example.com:80/demo/ssoclient/**

问:配置好单点登录后,访问应用后出现重定向次数过多

答:检查是否已经忽略/ssoclient/login这个地址的登录校验。


问:配置好单点登录后,登录时抛出Connection refused: connect[xxx]

答:检查SSOConfig.getTokenEndpointUrl()返回的地址,在web应用部署的服务器是否可以访问。


问:配置好单点登录后,登录时抛出HTTP Status 500 - parse json error

答:检查SSOConfig.getTokenEndpointUrl()的返回值:

  • 是否正确,如果json是一串html代码,很有可能是这个地址配错了。
  • 返回结果是否正确的json。

问:跳转到SSO注销完成后,为什么本地登录没有被注销?

答:SSO注销完成后,会根据应用在SSO注册的注销地址(logout_uri)向应用发注销请求,如果本地注销不了,需要检查:

  • 应用注册的注销地址是不是应用配置的本地注销地址
  • 应用本地注销的地址是否被其他拦截器拦截

问:配置好单点登录后,如果使用了反向代理,SDK登录重定向的url不对怎么办?

答:使用反向代理的时候,要配置代理:

  • 在请求头设置host请求头为代理服务器地址
  • 设置x-forwarded-proto请求头为访问协议(http或https)
  • 根据代理后的contextPath重写AbstractLoginServlet.getContextPathOfReverseProxy(req)方法

问:配置负载均衡后,为什么登录过程不停在sso和应用之间重定向导致重定向次数过多?

答:使用负载均衡后,由于从sso重定向回来的请求不一定能转发到最初请求登录的节点,因此需要启用粘性cookie,即保证登录请求从sso重定向回来之后可以回到最初 请求登录的节点。

问:如何设置登陆后重定向到哪个地址?

答:一般情况下,需要在重定向到/ssoclient/login这个url时指定,如:

response.sendRedirect(req.getContextPath()+"/ssoclient/login?return_url=http%3A%2F%2Flocalhost%3A8080%2Freturn_url")

这里return_url参数需要以queryString方式传递,并且需要进行url编码。

如果无法重定向到/ssoclient/login时指定,3.1.1版本后也可以通过配置config.setDefaultReturnUrl("http://localhost:8080/return_url"); 的方式指定默认的重定向地址。

问:如果服务端前有反向代理,重定向地址计算错误怎么办?

答:有两个方案:

  • 在反向代理设置两个请求头:x-forwarded-protox-forwarded-host,这两个请求头表示最终用户在浏览器访问这个服务的协议(http/https)和域名,SDK会优先根据这两个请求头计算重定向地址。
  • 重写AbstractLoginServlet.buildRedirectUri方法,按照实际情况拼凑重定向地址,注意,这个地址需要重定向参数return_url作为最终登陆完成后打开的页面,如:https://localhost:8080?return_url=http%3A%2F%2Fwww.baidu.com

sso-client's People

Contributors

evanfeng avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.