单点登陆(SSO)
修改记录
文件编号 |
版本号 |
拟制人/修改人 |
拟制/修改日期 |
更改理由 |
主要更改内容 (写要点即可) |
注1:每次更改归档文件,需填写此表 注2:文件第一次归档时,“主要更改内容”栏写无。 |
目录
1.单点登录接口文档
1.1.基础概念
1.2中心门户
1.2.1中心门户SSO流程图
1.2.2中心门户SSO 接口
1.3子系统接口
1.3.1子系统接口介绍
1.3.2 Token登录接口
1.3.3 Username+Token登录接口
2.单点登录实现的改造
2.1改造原理
2.2 SpringSecurity实现单点登录原理
2.3 Shiro 实现单点登录原理
SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
本文中的 中心门户 指的是一个系统,在一个企业下有许多系统,企业用户在登录这个系统以后,不需要输入账号密码,就可以跳转到他想要去的 子系统 并且完成登录 ,本文档就用于实现,改造,完成该功能
用户在登录 中心门户 后,点击网页上显示的子系统,触发前端的点击事件,将调用后端的登录接口
接口信息 |
http://.../api/sso/ssoToken |
接口方式 |
public AjaxResult test(HttpServletRequest request, HttpServletResponse response) |
接口类型 |
Restful |
接口编码 |
UTF-8 |
调用方式 |
POST |
调用端 |
中心门户前端页面 |
(本借口由于接收的参数较多,使用Resquest来做形参接受参数,在方法体内进行取值,调用相应的接口完成功能的实现)
具体接收的参数有:
参数名 |
参数类型 |
参数备注 |
username |
String |
当前登录用户的用户名 |
token |
String |
当前登录用户的TOKEN |
webid |
String |
要实现SSO的子系统的url |
在中心门户实现SSO后,系统会记录本次SSO的结构,存放在数据库中,表名为tab_single_point_log
字段名 |
类型 |
注释 |
single_point_id |
Int |
主键 |
user_id |
Int |
单点登录用户id |
web_id |
Int |
目标系统id |
token |
varchar |
实现SSO的TOKEN |
single_point_statu |
Int |
0正常 1失败 |
single_point_time |
Datetime |
完成SSO时间 |
single_point_describe |
varchar |
SSO事件描述 |
子系统完成SSO登录共有两个接口,两个接口的功能都是实现了自定义登录,详情见1.2.1的流程图,其大致思路为:先采用Token登录,当Token完成不了系统的验证就采用Username+Token登录
接口信息 |
http://.../user/loginByToken |
接口方式 |
public ResponseResult loginByToken (@RequestBody UserSso userSso) |
接口类型 |
Restful |
接口编码 |
UTF-8 |
调用方式 |
POST |
调用端 |
中心门户 |
返回值ResponseResult:
参数 |
类型 |
备注 |
Code |
Integer |
返回代码 200成功 其他失败 |
Msg |
String |
返回信息 |
Data |
泛型 |
携带的返回参数 |
接口信息 |
http://.../user/loginByUserNameAndToken |
接口方式 |
public ResponseResult loginByUserNameAndToken (@RequestBody UserSso userSso) |
接口类型 |
Restful |
接口编码 |
UTF-8 |
调用方式 |
POST |
调用端 |
中心门户 |
返回值ResponseResult:
参数 |
类型 |
备注 |
Code |
Integer |
返回代码 200成功 其他失败 |
Msg |
String |
返回信息 |
Data |
泛型 |
携带的返回参数 |
本文档实现SSO的原理,其实是通过浏览器Cookies实现,考虑到Cookie存在浏览器中,可以被不同的系统读取,实现跨域,完成登录的验证。同时需要改造系统自定义认证和授权,目前主流的框架有SpringSecurity和Shiro,本文档就两种热门框架,以前后端分离的主流系统,完成了SSO demo的认证。
由于各系统的授权系统不一致,本文就没有过多描述,实现方法原理也很简单,在用户完成授权时将用户权限加入到认证体中,即可完成。
首先SpringSecurity工作过程原理:
几个重要的组件
Authentication:(接口):用户输入账号密码可以是Authentication 手机号+验证码也可以是Authentication 所以Authenticaion是一次登陆请求操作的载体。可以看一个Authenticaion的一个实现类例子
principal:用户名用于可唯一标识用户信息的属性
Credentials:存储密码作为用户端鉴权凭证
构造方法对比:
构造方法对比:
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection
extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
第一个构造方法是用来构造一个未被认证过的实体类
第二个构造方式是返回一个已经被认证过的实体类 表示已经认证成功
Provider:Provider是AuthenticaionManger下中List
他是对具体的Authentication的认证管理,来认证这个Authentication的
认证成功,就返回带认证的Authencation 认证失败 抛出异常
方法:
public interface AuthenticationProvider {
//验证
Authentication authenticate(Authentication authentication) throws AuthenticationException;
//表面支持哪种Authenticaion
boolean supports(Class> authentication);
}
Filter过滤
例子:
FilterChain
方法filterChain.doFilter(request,response);
SecurityConfig:
配置类 配置具体的配置
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于登录接口 允许匿名访问
.antMatchers("/user/login").anonymous()
.antMatchers("/user/getcode").anonymous()
.antMatchers("/user/loginByCode").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
;
}
注入provider和绑定
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userDetailsService)
.passwordEncoder(this.passwordEncoder())
.and()
.authenticationProvider(userCodeAuthenticationProvider())
.authenticationProvider(authenticationProvider());
}
总结SpringSecurity验证流程:
1.请求接口 接受参数
2.包装请求参数 交给authenticationManager中验证
3.包装好的Authentication传入authenticationManager,在List
正确的话就返回以及认证过的Authenticaion,发送到Service层 最后返回登陆成功的消息
FilterChina在这一切之前
因此SpringSecurity完成SSO就是要自定义token登录和username+token登录,具体方法可以看demo中的
Shiro和SpringSecurity实现过程大差不差,重要的几个方法:
Subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(user.getUsername(),user.getPassword()));
/**
public class UsernameAndToken implements AuthenticationToken {
private String token;
private String username;
**/
public class UsernameAndTokenRealm extends AuthorizingRealm {
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernameAndToken;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernameAndToken shiroAuthToken = (UsernameAndToken) authenticationToken;
if(认证成功)
return new SimpleAuthenticationInfo(username,token, "UsernameAndToken");
}
else{
System.out.println("invalid username");
throw new AuthenticationException("认证失败");
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
授权
}
}
自定义Realm以及Token便可以完成认证