springboot 单点登录(详细代码)

单点登录,就是在一个系统登录后,在它的关联系统也不用重新登录了。例如:你成功登录了淘宝,那么在天猫也就成功登录了;同理,退出也是一样的。在天猫退出账号,在淘宝的账号也就退出了。(咳咳,同一浏览器内)

实现单点登录,主要就是利用同域名传递 cookie 中的登录用户信息。以下是一种实现方式,仅供参考!

准备工作

1)
系统:win10
IDE:sts4
springboot2.2.4.RELEASE、 jdk8、 maven3.3.9

在本例中,创建了4个 springboot 项目,一个专用来处理登录(login.sso.com),其他都是关联系统,把 sys1.sso.com 当做是主系统。

如果用户首先在 sys1 系统的首页浏览,此时点击登录,则跳转到登录系统处理登录逻辑。登录成功后,会跳转到 sys1 系统的首页,并且是已登录状态。
同理用户在 sys2 系统的首页浏览,此时点击登录,则跳转到登录系统处理登录逻辑。登录成功后,会跳转到 sys2 系统的首页,并且是已登录状态。
那如果一开始就是没有在其他系统浏览,而是直接到登录系统进行登录呢。那这种情况登录成功后,本例会跳转到 sys1 的首页,即设定 sys1 为主系统的意思

2)域名映射
修改 hosts 文件,路径:C:\Windows\System32\drivers\etc
在这里插入图片描述

登录系统

登录系统只有一个登录界面和处理登录信息的逻辑
1)pom 依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

没有使用数据库,用户信息是写死的,所以只需要这两个依赖就够了。

2)登录界面 login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sso-login</title>
</head>
<body>
	<h3>这里是登录页面</h3>
	<p style="color:red;" th:text="${session.msg}"></p>
	<form action="/login" method="post">
		用户名:<input type="text" name="uname"/><br/>
		密码:<input type="password" name="upwd"/><br/>
		<button type="submit">登录</button>
	</form>
</body>
</html>

3)控制层及其他

public class User {
    
    

	private String uname;
	private String upwd;
	public User(String uname, String upwd) {
    
    
		super();
		this.uname = uname;
		this.upwd = upwd;
	}
	public String getUname() {
    
    
		return uname;
	}
	public void setUname(String uname) {
    
    
		this.uname = uname;
	}
	public String getUpwd() {
    
    
		return upwd;
	}
	public void setUpwd(String upwd) {
    
    
		this.upwd = upwd;
	}
	
}
/**
 * 页面跳转控制
 * @author Xiaogf
 */
@Controller
@RequestMapping("/page")
public class PageController {
    
    

	/**
	 * 跳转到登录页
	 * @return
	 */
	@RequestMapping("/login")
	public String toLogin(@RequestParam(required = false, defaultValue = "")String target,
						  HttpSession session, @CookieValue(required = false, value = "TOKEN")Cookie cookie) {
    
    
		
		// 如果是已登录的用户再次访问登录页面,就要重定向
		if(cookie != null) {
    
    
			String value = cookie.getValue();
			User user = LoginCacheUtil.loginUser.get(value);
			if(user != null) {
    
    
				return "redirect:" + target;
			}
		}
		// 用于登录成功后重定向地址
		session.setAttribute("target", target);
		return "login";
	}

	/**
	 * 用户直接访问登录页
	 * @return
	 */
	@RequestMapping("/index")
	public String loginIndex() {
    
    
		return "login";
	}
}
@Controller
public class LoginController {
    
    

	@PostMapping("/login")
	public String doLogin(User user, HttpSession session, HttpServletResponse response) {
    
    
		// 校验用户名密码
		if(user.getUname().equals("xiao")&&user.getUpwd().equals("123")) {
    
    
			// 保存用户登录信息
			String token = UUID.randomUUID().toString();
			System.out.println("login.token===" + token);
			Cookie cookie = new Cookie("TOKEN", token);
			//设置域名,实现数据共享
			cookie.setDomain("sso.com");
			// 把cookie写到客户端
			response.addCookie(cookie);
			LoginCacheUtil.loginUser.put(token, user);
		}else {
    
    
			session.setAttribute("msg", "用户名或密码错误");
			return "login";
		}
		//登录信息校验成功,重定向到原来的系统
		String targetUrl = (String) session.getAttribute("target");
		//如果是直接从登录系统登录的,校验成功后默认跳转到主系统 sys1 的首页
		if(StringUtils.isEmpty(targetUrl)) {
    
    
			targetUrl = "http://sys1.sso.com:8081/index";
		}
		return "redirect:" + targetUrl;
	}
	
	/**
	 * 其他同域名的系统通过该接口获取登录用户的信息
	 * @param token
	 * @return
	 */
	@RequestMapping("/info")
	@ResponseBody
	public ResponseEntity<User> getUserInfo(String token){
    
    
		if(!StringUtils.isEmpty(token)) {
    
    
			User user = LoginCacheUtil.loginUser.get(token);
			return ResponseEntity.ok(user);
		}else {
    
    
			return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
		}
	}
}
public class LoginCacheUtil {
    
    
	//模拟系统的数据缓存,如 Redis 之类的
	public static Map<String, User> loginUser = new HashMap<>();
}

子系统

本例中 sys1,sys2,sys3 这几个系统的页面和登录处理逻辑都是一样的,注意地址和端口改改就行。这里的 login 是 8080,sys1 是 8081,sys2 是 8082,sys3 是 8083
1)pom 文件同上
2)系统首页界面 index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sso-sys1</title>
</head>
<body>
	<h1>这里是 sso-sys1 首页</h1>
	<p th:unless="${session.loginUser==null}">
		<span style="color:deepskyblue;" th:text="${session.loginUser.uname}"></span> 已登录
	</p>
	<a th:if="${session.loginUser==null}" href="http://login.sso.com:8080/page/login?target=http://sys1.sso.com:8081/index">登录</a>
	<a th:unless="${session.loginUser==null}" href="/loginOut">退出</a>
</body>
</html>

登录的路径中,需要携带本系统的地址,以便登录成功后可以跳转回本系统首页

3)控制层

@Controller
public class Sys1Controller {
    
    

	@Autowired
	private RestTemplate restTemplate;
	//获取登录用户信息的 URL,就是访问登录系统的 info 接口
	private final String LOGIN_INFO_URL="http://login.sso.com:8080/info?token=";
	
	@RequestMapping("/index")
	public String toIndex(@CookieValue(required = false, value = "TOKEN")Cookie cookie, HttpSession session) {
    
    
		if(cookie != null) {
    
    
			//获取cookie 中 TOKEN 的值
			String token = cookie.getValue();
			System.out.println("sys1.token===" + token);
			if(!StringUtils.isEmpty(token)) {
    
    
				//如果 TOKEN 中有值,则发送请求获取登录用户的信息,并存到该系统的 session 缓存中
				Map object = restTemplate.getForObject(LOGIN_INFO_URL + token, Map.class);
				System.out.println("sys1.object===" + object);
				session.setAttribute("loginUser", object);
			}else {
    
    
				//如果 TOKEN 没有值,代表没有登录或者已经退出,应该清除缓存中的登录用户信息
				session.setAttribute("loginUser", null);
			}
		}
		return "index";
	}
	
	@RequestMapping("/loginOut")
	public String loginOut(HttpSession session, HttpServletResponse response) {
    
    
		//清除session中用户登录信息
		session.setAttribute("loginUser", null);
		//清除cookie中 TOKEN 的值
		Cookie cookie = new Cookie("TOKEN", "null");
		cookie.setDomain("sso.com");
		response.addCookie(cookie);
		//重定向到该系统的首页
		return "redirect:/index";
	}
}

4)启动类需要加入一个 Bean

@SpringBootApplication
public class SsoSys1Application {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(SsoSys1Application.class, args);
	}
	
	@Bean
	public RestTemplate restTemplate() {
    
    
		return new RestTemplate();
	}

}

sys2 与 sys3 的 index.html 中地址和端口需要稍微改一下,其他一样,这里就不重复贴出来了。

测试

启动登录系统、sys1系统,sys2 系统和 sys3 系统,分别在不同标签页访问 sys1.sso.com:8081/index 、sys2.sso.com:8082/index 、sys3.sso.com:8083/index ,
点击登录,都跳转到了登录系统 login.sso.com
当在其中一个系统登录成功时,刷新另外一个系统,会发现也已经登录了。
当在其中一个系统退出后,刷新另外一个系统,会发现也已经退出了。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Alias_fa/article/details/104446542