• 理解单点登录


    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:

    1. package com.pshdhx.gulimalltestssoclient1.controller;
    2. import org.springframework.http.ResponseEntity;
    3. import org.springframework.stereotype.Controller;
    4. import org.springframework.ui.Model;
    5. import org.springframework.util.StringUtils;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.RequestParam;
    8. import org.springframework.web.client.RestTemplate;
    9. import javax.servlet.http.HttpSession;
    10. import java.util.ArrayList;
    11. import java.util.List;
    12. /**
    13. * @author pshdhx
    14. * @date 2022-08-29 20:55
    15. * @Des
    16. * @Method
    17. * @Summary
    18. */
    19. @Controller
    20. public class LoginController {
    21. @GetMapping("/employees")
    22. public String employees(Model model, HttpSession session,
    23. @RequestParam(name="token",required = false) String token){
    24. if(!StringUtils.isEmpty(token)){
    25. //根据token去sso认证中心去获取信息
    26. RestTemplate restTemplate = new RestTemplate();
    27. ResponseEntity entity = restTemplate.getForEntity("http://sso.com:8080/userinfo?token=" + token, String.class);
    28. session.setAttribute("loginUser", entity.getBody());
    29. }
    30. Object loginUser = session.getAttribute("loginUser");
    31. if(loginUser == null && token == null){
    32. // 未登录,跳转认证服务器登录
    33. return "redirect:http://sso.com:8080/login.html?redirect_url=http://client1.com:8081/employees";
    34. }else {
    35. // 登录状态显示
    36. List emps = new ArrayList<>();
    37. emps.add("张三");
    38. emps.add("李四");
    39. model.addAttribute("emps", emps);
    40. return "employees";
    41. }
    42. }
    43. }

    配置信息:

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

    客户端2:

    1. package com.pshdhx.gulimalltestssoclient2.controller;
    2. import org.springframework.http.ResponseEntity;
    3. import org.springframework.stereotype.Controller;
    4. import org.springframework.ui.Model;
    5. import org.springframework.util.StringUtils;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.RequestParam;
    8. import org.springframework.web.client.RestTemplate;
    9. import javax.servlet.http.HttpSession;
    10. import java.util.ArrayList;
    11. import java.util.List;
    12. /**
    13. * @author pshdhx
    14. * @date 2022-08-29 20:55
    15. * @Des
    16. * @Method
    17. * @Summary
    18. */
    19. @Controller
    20. public class LoginController {
    21. /**
    22. * 需要登录状态访问
    23. */
    24. @GetMapping(value = "/employees")
    25. public String employees(Model model, HttpSession session,
    26. @RequestParam(name = "token", required = false) String token) {
    27. if (!StringUtils.isEmpty(token)) {
    28. // 根据token去sso认证中心获取用户信息
    29. RestTemplate restTemplate = new RestTemplate();
    30. ResponseEntity entity = restTemplate.getForEntity("http://sso.com:8080/userinfo?token=" + token, String.class);
    31. session.setAttribute("loginUser", entity.getBody());
    32. }
    33. Object loginUser = session.getAttribute("loginUser");
    34. if (loginUser == null && token == null) {
    35. // 未登录,跳转认证服务器登录
    36. return "redirect:http://sso.com:8080/login.html?redirect_url=http://client2.com:8082/employees";
    37. } else {
    38. // 登录状态显示
    39. List emps = new ArrayList<>();
    40. emps.add("张三");
    41. emps.add("李四");
    42. model.addAttribute("emps", emps);
    43. return "employees";
    44. }
    45. }
    46. }
    1. server.port=8082
    2. server.servlet.session.timeout=30m
    3. spring.session.store-type=redis
    4. spring.redis.host=xxxxxxxxxxxxx

    认证服务端:

    1. package com.pshdhx.gulimall.testssoserver.controller;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.data.redis.core.StringRedisTemplate;
    4. import org.springframework.stereotype.Controller;
    5. import org.springframework.ui.Model;
    6. import org.springframework.util.StringUtils;
    7. import org.springframework.web.bind.annotation.*;
    8. import javax.servlet.http.Cookie;
    9. import javax.servlet.http.HttpServletResponse;
    10. import java.util.UUID;
    11. /**
    12. * @author pshdhx
    13. * @date 2022-08-29 21:06
    14. * @Des
    15. * @Method
    16. * @Summary
    17. */
    18. @Controller
    19. public class LoginServerController {
    20. @Autowired
    21. StringRedisTemplate redisTemplate;
    22. /**
    23. * 供其他应用获取用户信息的接口
    24. * @param token
    25. * @return
    26. */
    27. @ResponseBody
    28. @GetMapping("/userinfo")
    29. public String userInfo(@RequestParam("token") String token) {
    30. String username = redisTemplate.opsForValue().get(token);
    31. return username;
    32. }
    33. /**
    34. * 访问登录页
    35. * @param url 登录成功回调页
    36. * @param token cookie值
    37. */
    38. @GetMapping(value = "/login.html")
    39. public String login(@RequestParam("redirect_url") String url, Model model,
    40. @CookieValue(value = "sso_token", required = false) String token) {
    41. // 根据token获取用户信息【这一块是针对于客户端2进行共享的,因为客户端1设置了cookie,所以客户端2才能共享到此sso_token】
    42. if (!StringUtils.isEmpty(token)) {
    43. String username = redisTemplate.opsForValue().get(token);
    44. if (!StringUtils.isEmpty(username)) {
    45. // token正确,已登录状态,跳转回客户端【当前访问客户端共享了其他客户端的登录状态】
    46. return "redirect:" + url + "?token=" + token;
    47. }
    48. }
    49. // 不存在sso_token,未登录返回登录页,并将回调地址链路下传
    50. model.addAttribute("url", url);
    51. return "login";
    52. }
    53. /**
    54. * 登录
    55. * @param url 登录成功回调页
    56. */
    57. @PostMapping(value = "/doLogin")
    58. public String doLogin(String username, String password, String url,
    59. Model model, HttpServletResponse response) {
    60. if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
    61. // 登录成功,跳转回调页
    62. String token = UUID.randomUUID().toString().replace("-", "");
    63. // token作为key,用户信息作为value存入redis中
    64. redisTemplate.opsForValue().set(token, username);
    65. // 在sso.com域名下设置cookie,使得不同客户端访问单点登录时可以带上cookie值成功登录
    66. Cookie cookie = new Cookie("sso_token", token);
    67. response.addCookie(cookie);
    68. return "redirect:" + url + "?token=" + token;
    69. }
    70. // 登录失败
    71. model.addAttribute("url", url);
    72. return "login";
    73. }
    74. }
    1. server.port=8080
    2. server.servlet.session.timeout=30m
    3. spring.redis.host=xxxxxxxxxxxxxxxxxxxxxxx

     

    1. html>
    2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>登录页title>
    6. head>
    7. <body>
    8. <form action="/doLogin" method="post">
    9. 用户名:<input type="text" name="username"/><br/>
    10. 密码:<input type="password" name="password"/><br/>
    11. <input type="hidden" name="url" th:value="${url}">
    12. <input type="submit" value="登录">
    13. form>
    14. body>
    15. html>

    单点登录代码学习:

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

  • 相关阅读:
    在公司项目中使用git的简单手册
    AI: 2021 年人工智能前沿科技报告(更新中……)
    AtCoder Beginner Contest 260 A~F 题解
    进销存管理对于企业的意义
    C++多态(超级详细版)
    记录模型压缩概述
    Win10系统edge浏览器打不开怎么解决
    pcl--第七节 点云配准
    前端开发工程师工作梳理
    初探Golang语法巩固复习
  • 原文地址:https://blog.csdn.net/pshdhx/article/details/126609961