当前位置: 代码迷 >> 综合 >> jsonp、cookies实现单点登录(完全跨域)
  详细解决方案

jsonp、cookies实现单点登录(完全跨域)

热度:17   发布时间:2023-12-05 20:08:28.0

单点登录实现的目标:在一个套系统的任意一个系统中登录之后,访问其他子系统能直接登录。

在同一主域名下的所有系统可以共享主域名的cookies,所以再一台服务器中登录之后,将token信息存入到主域名下的cookies中,任意一个子系统访问会自动带上这个token信息,能达到单点登录的效果。

这里主要实现完全跨域的情况下如何实现单点登录:也就是两个系统的域名完全分离,不能共用cookies信息,所以如何在子系统间共享或者传输token信息是最大的问题。

有一个解决方案是:在一个统一登录界面登录之后,将token信息放到重定向的url里面,重定向之后就可以解析url路径参数中的token信息,然后存到自己域名下的token中,但是这个不太优雅,容易造成token的窃取,比如重定向一个url是危险服务器的地址,登录成功之后token信息将会传入到指定服务器中,这就造成token泄露。

这里利用jsonp实现跨域请求鉴权,返回token信息之后存入到自己域名下,之后子系统每次请求都先到统一认证中心进行token检验。使用jsonp也会造成json劫持,这就需要进行Referer验证之类的防范措施了。

具体流程:

1.统一在一个用户中心(UC)登录,UC会将token信息存入到UC域名下的cookies中。

2.UC中另外写一个鉴权接口来返回token信息,验证请求方合法性

3.子系统通过JSONP跨域请求UC系统的鉴权接口,因为是JSONP访问,所以会携带上UC的cookies,鉴权将会返回UC中存的token信息,然后子系统将这个token存入到子系统的cookies中

4.每次业务请求都带上这个cookies

5.子系统请求UC服务验证token的正确性(这里可以是微服务调用,dubbo调用之类的)

6.token有效返回信息。

涉及的接口:

UC登录接口:登录成功返回token信息,同时将信息存入到服务器中

private static ConcurrentHashMap<String, String> tokens = new ConcurrentHashMap<>();@GetMapping("/login")public String login(String username, String password) {if (username == null)return "登录失败";String token = generatorUID();response.addCookie(new Cookie("x-token", token));tokens.putIfAbsent(token, username);return "hello:" + username;}

UC鉴权接口(JSONP访问):UC的auth接口将解析cookies中的token信息,如果合法,将返回token和用户信息

/*** 返回token信息,用于跨域的token传递* 检验跨域的白名单列表,防止json劫持* @param callback* @return*/@GetMapping("/auth")public String auth(String callback) {if (notAllowAuth()) {return "禁止该网点单点登录";}String token = getToken(request.getCookies());if (token == null) {return "用户未登录";}String username = tokens.get(token);if (username == null) {return "用户未登录";}return  callback + "('"+ username +"','" + token + "')";}

UC鉴权接口(业务系统访问):检验token是否有效,如果有效返回用户信息

	/*** 通过token鉴权* @param token* @return*/@GetMapping("/authForToken")public String authForToken(String token) {String username = tokens.get(token);if (username == null) {return "用户未登录";}return username;}

Sys业务系统请求token信息(JSONP跨域请求UC):返回token信息存入到业务系统的域名下

sys/index.html

<script>function call(username, token) {console.log(username);console.log(token);document.cookie="x-token="+token;}
</script>
<script type="text/javascript" src="http://localhost:8080/uc/auth?callback=call"></script>

Sys业务接口:携带token信息并到UC验证token信息有效性,如果合法将返回业务数据,例子中是直接get访问UC鉴权接口实现远程调用,(这种鉴权比较频繁,所以长连接调用更合适,如dubbo)

	@GetMapping("/hello")public String hello() {Cookie[] cookies = request.getCookies();if (cookies == null) {return "用户未登录";}String token = null;for (Cookie cookie : cookies) {if (cookie.getName().equals("x-token")) {token = cookie.getValue();}}ResponseEntity<String> username = template.getForEntity("http://localhost:8080/uc/authForToken?token=" + token, String.class);String str = username.getBody();if (str == null || str.equals("用户未登录")) {return "用户未登录";}return "hello:" + str;}

效果:

UC系统:localhost:8080/uc

Sys子系统:127.0.0.1:8085/sys

未登录的时候,UC鉴权返回未登录,sys子系统返回未登录

UC登录:登录成功

直接访问子系统(不做任何页面跳转):访问127.0.0.1:8085/sys,成功通过JSONP拿到token信息

访问业务接口试试:127.0.0.1:8085/sys/hello,成功单点登录,返回业务数据

项目采用springboot搭建:地址:https://github.com/1510460325/cas

  相关解决方案