0
点赞
收藏
分享

微信扫一扫

理解单点登录


1、目的

跨域名和跨作用域实现多个系统或者是服务之间,实现一处登录,处处登录。

2、构成

客户端1:用于模拟系统1;

客户端2:用于模仿系统2;

认证服务端server:用于用户认证;

3、思路:

1、客户端1想要访问客户端1(http://client1.com:8081/employees)的内容,必须先登录。

2、首次访问客户端1的内容,没有登录信息,必须先进行认证登录,所以就跳转去了认证服务端server的认证页面进行认证登录。

【跳转的同时,带上一个参数,就是客户端1访问接口的地址:

return "redirect:http://sso.com:8080/login.html?redirect_url=http://client1.com:8081/employees";

3、认证服务端server,接收到了客户端1的跳转地址,先验证浏览器有没有sessin和redis有没有sso_token,如果没有,就跳转到登录页面;【留下个彩蛋,供客户端2进行使用

4、输入账号密码后,认证服务登录成功,【设置了cookie和redis的sso_token】所以就跳转到了客户端1的访问接口,因为已经认证过了,所以可以直接进行内容的访问。

5、如果是说客户端2进行了接口(http://client2.com:8082/employees)内容的访问,但是客户端2什么登录信息也没有,所以跳转到了认证服务端server,到了第三步,此时用户1已经在第四步设置了sso_token和cookie,所以用户2是有sso_token和cookie【就是步骤3的彩蛋】的,所以直接跳转到客户端2的访问接口。

代码信息:

客户端1:

package com.pshdhx.gulimalltestssoclient1.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;

/**
* @author pshdhx
* @date 2022-08-29 20:55
* @Des
* @Method
* @Summary
*/
@Controller
public class LoginController {
@GetMapping("/employees")
public String employees(Model model, HttpSession session,
@RequestParam(name="token",required = false) String token){
if(!StringUtils.isEmpty(token)){
//根据token去sso认证中心去获取信息
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> entity = restTemplate.getForEntity("http://sso.com:8080/userinfo?token=" + token, String.class);
session.setAttribute("loginUser", entity.getBody());
}
Object loginUser = session.getAttribute("loginUser");
if(loginUser == null && token == null){
// 未登录,跳转认证服务器登录
return "redirect:http://sso.com:8080/login.html?redirect_url=http://client1.com:8081/employees";
}else {
// 登录状态显示
List<String> emps = new ArrayList<>();
emps.add("张三");
emps.add("李四");
model.addAttribute("emps", emps);
return "employees";
}
}
}

配置信息:

server.port=8081
server.servlet.session.timeout=30m
spring.session.store-type=redis
spring.redis.host=xxxxxxxxxxxxxxxxxxxxxxx

客户端2:

package com.pshdhx.gulimalltestssoclient2.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;

/**
* @author pshdhx
* @date 2022-08-29 20:55
* @Des
* @Method
* @Summary
*/
@Controller
public class LoginController {

/**
* 需要登录状态访问
*/
@GetMapping(value = "/employees")
public String employees(Model model, HttpSession session,
@RequestParam(name = "token", required = false) String token) {
if (!StringUtils.isEmpty(token)) {
// 根据token去sso认证中心获取用户信息
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> entity = restTemplate.getForEntity("http://sso.com:8080/userinfo?token=" + token, String.class);
session.setAttribute("loginUser", entity.getBody());
}
Object loginUser = session.getAttribute("loginUser");
if (loginUser == null && token == null) {
// 未登录,跳转认证服务器登录
return "redirect:http://sso.com:8080/login.html?redirect_url=http://client2.com:8082/employees";
} else {
// 登录状态显示
List<String> emps = new ArrayList<>();
emps.add("张三");
emps.add("李四");
model.addAttribute("emps", emps);
return "employees";
}

}
}

server.port=8082
server.servlet.session.timeout=30m
spring.session.store-type=redis
spring.redis.host=xxxxxxxxxxxxx

认证服务端:

package com.pshdhx.gulimall.testssoserver.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
* @author pshdhx
* @date 2022-08-29 21:06
* @Des
* @Method
* @Summary
*/
@Controller
public class LoginServerController {
@Autowired
StringRedisTemplate redisTemplate;

/**
* 供其他应用获取用户信息的接口
* @param token
* @return
*/
@ResponseBody
@GetMapping("/userinfo")
public String userInfo(@RequestParam("token") String token) {
String username = redisTemplate.opsForValue().get(token);
return username;
}

/**
* 访问登录页
* @param url 登录成功回调页
* @param token cookie值
*/
@GetMapping(value = "/login.html")
public String login(@RequestParam("redirect_url") String url, Model model,
@CookieValue(value = "sso_token", required = false) String token) {
// 根据token获取用户信息【这一块是针对于客户端2进行共享的,因为客户端1设置了cookie,所以客户端2才能共享到此sso_token】
if (!StringUtils.isEmpty(token)) {
String username = redisTemplate.opsForValue().get(token);
if (!StringUtils.isEmpty(username)) {
// token正确,已登录状态,跳转回客户端【当前访问客户端共享了其他客户端的登录状态】
return "redirect:" + url + "?token=" + token;
}
}
// 不存在sso_token,未登录返回登录页,并将回调地址链路下传
model.addAttribute("url", url);
return "login";
}

/**
* 登录
* @param url 登录成功回调页
*/
@PostMapping(value = "/doLogin")
public String doLogin(String username, String password, String url,
Model model, HttpServletResponse response) {
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
// 登录成功,跳转回调页
String token = UUID.randomUUID().toString().replace("-", "");
// token作为key,用户信息作为value存入redis中
redisTemplate.opsForValue().set(token, username);
// 在sso.com域名下设置cookie,使得不同客户端访问单点登录时可以带上cookie值成功登录
Cookie cookie = new Cookie("sso_token", token);
response.addCookie(cookie);
return "redirect:" + url + "?token=" + token;
}
// 登录失败
model.addAttribute("url", url);
return "login";
}
}

server.port=8080
server.servlet.session.timeout=30m
spring.redis.host=xxxxxxxxxxxxxxxxxxxxxxx

 

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页</title>
</head>
<body>
<form action="/doLogin" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="hidden" name="url" th:value="${url}">
<input type="submit" value="登录">
</form>
</body>
</html>

单点登录代码学习:

xxl-sso: 一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。 拥有"轻量级、分布式、跨域、Cookie+Token均支持、Web+APP均支持"等特性;。现已开放源代码,开箱即用。

举报

相关推荐

0 条评论