单点登录SSO的三种使用场景
2020-02-25 本文已影响0人
求索
在产品迭代升级过程中,单点登录机制是一个关键节点。单点登录涉及安全、缓存、多账号共存、用户冲突等各类问题。
单点登录1.0版本
单点登录.png
在父应用(单点登录服务器)记录登录token,一般使用cookie记录。用户登录时先跳转到单点登录服务器去获取token,然后使用token登录子应用。
单点登录服务实现:
String token = CookieUtils.getCookie(request, "token");
if (token == null) {
return goLoginPath(redirect_uri, appid, pmsuite_state, request, response);
} else {
UserAccount loginUser = tokenManager.validate(token);
if (loginUser != null ) {
request.setAttribute("user", loginUser);
return "redirect:" + WebUtils.formatAuthCallbackUrl(redirect_uri, token, pmsuite_state);
} else {
return goLoginPath(redirect_uri, appid, pmsuite_state, request, response);
}
}
客户端实现:
@RequestMapping("/tokenLogin")
public String tokenLogin(HttpServletRequest request, HttpServletResponse response, @RequestParam String token) {
AuthenticationToken authenticationToken = new UsernamePasswordToken(token);
Subject subject = SecurityUtils.getSubject();
subject.login(authenticationToken);
return "home";
}
单点登录2.0版本
1.0版本存在如下问题
- 不能返回原始登录地址
- 多账号登录冲突
- 用户踢出同步
- 用户缓存
登录整体使用shiro框架,扩展ShiroFilterFactoryBean,通过AbstractShiroFilter机制实现tokenLogin。
@Override
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
super.doFilterInternal(servletRequest, servletResponse, chain);
}
@Override
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) throws IOException, ServletException {
if (ShiroHelper.isLoginSubmission(request, response)) {
....
AuthenticationToken token = createToken(request, response);
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession(false);
if (session != null) { ShiroSessionDao.afterSessionDelete(session.getId().toString());
}
subject.login(token);
} else {
super.executeChain(request, response, origChain);
}
}
同时引入redis、集成redis+session,实现session共享。保障多机共享。
单点登录3.0版本
随着前端技术不断发展,通过直接跳转地址的模式实现单点登录和前端接口调用的逻辑格格不入。基于前后端实现单点登录的方案也有很多,其中使用jwt实现就是一种方式。其实现原理:
jwt单点登录.png
服务端接口代码登录:
String token= jwtServerService.generateJWT(account,request.getHeader("user-agent"));
addTokenInCookie(token,request,response);
addLog(log, result);
result.setAuthCode(token);
服务端接口-验证登录状态并返回token:
@GetMapping("/checkLogin")
public ApiResult<String> checkLogin(HttpServletRequest request) {
String token = CookieUtils.getCookie(request,'tokenName');
ApiResult<String> result =new ApiResult<>();
if(StringUtils.isNullOrEmpty(token)){
return result.error("未登录");
}
JwtResult jwtResult= jwtClientService.parseJwt(token);
if(!jwtResult.equalsSuccess()){
return result.error(jwtResult.getMsg());
}
result.setData(token);
return result;
}
客户端:
@ResponseBody
@RequestMapping(‘tokenLogin’)
public ApiResult loginByJwt(HttpServletRequest request, HttpServletResponse response, @RequestParam String token) {
if (StringUtils.isNullOrEmpty(token)) {
token = request.getHeader(this.suiteProperties.jwt.getHeader());
}
AuthenticationToken authenticationToken = new UsernamePasswordToken(token, this.getSecrect());
Subject subject = SecurityUtils.getSubject();
subject.login(authenticationToken);
return new ApiResult();
}
单点登录的实现方式有很多种,只要能满足当前项目的需求,什么方案都可以。当然有时间的化,对技术还是要有工匠精神。
当下任务是我们的使命,精益求精是我们的职业态度。