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

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

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

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

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

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

具体流程:

jsonp、cookies实现单点登录(完全跨域)_第1张图片

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 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


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 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子系统返回未登录

jsonp、cookies实现单点登录(完全跨域)_第2张图片

jsonp、cookies实现单点登录(完全跨域)_第3张图片

UC登录:登录成功

jsonp、cookies实现单点登录(完全跨域)_第4张图片

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

jsonp、cookies实现单点登录(完全跨域)_第5张图片

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

jsonp、cookies实现单点登录(完全跨域)_第6张图片

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

你可能感兴趣的:(java基础,分布式)