# SSO单点登录demo **Repository Path**: N_code/sso-dandiandenglu-demo ## Basic Information - **Project Name**: SSO单点登录demo - **Description**: SSO单点登录demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2022-05-17 - **Last Updated**: 2024-01-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: 其他 ## README # 1.单点登录介绍 [什么是单点登录(原理与实现简介)_xiaoguan_liu的博客-CSDN博客_单点登录](https://blog.csdn.net/xiaoguan_liu/article/details/91492110) > 单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。 ``` 一处登陆,处处登录 ``` ![image-20220127215924300](Markdown_Yong_JPG/image-20220127215924300.png) # 2.单点登录框架介绍 https://gitee.com/xuxueli0323/xxl-sso?_from=gitee_search # 3.单点登录框架的使用 ## 单点登录服务器端启动 ### 1.克隆下来这个仓库,修改配置文件 ![image-20220127224745990](Markdown_Yong_JPG/image-20220127224745990.png) > **每个子项目的配置文件都要修改** ![image-20220127232522341](Markdown_Yong_JPG/image-20220127232522341.png) ### 2.修改系统host ![image-20220128001619719](Markdown_Yong_JPG/image-20220128001619719.png) ### 3.将克隆下来的项目打包 ``` mvn clean package -Dmaven.skip.test=true ``` ![image-20220127225801665](Markdown_Yong_JPG/image-20220127225801665.png) ### 4.启动服务 ``` java -jar xxl-sso-server-1.1.1-SNAPSHOT.jar ``` ![image-20220127230424795](Markdown_Yong_JPG/image-20220127230424795.png) ### 5.浏览器登录进去 ![image-20220127232746779](Markdown_Yong_JPG/image-20220127232746779.png) ``` http://xxlssoserver.com:8080/xxl-sso-server/login ``` ![image-20220127233120500](Markdown_Yong_JPG/image-20220127233120500.png) ## 单点登录客户端启动 ### 1.启动 > java -jar xxl-sso-web-sample-springboot-1.1.1-SNAPSHOT.jar --server.port=8081 ![image-20220127233500664](Markdown_Yong_JPG/image-20220127233500664.png) #### 启动两个客户端,端口号让不一样 > java -jar xxl-sso-web-sample-springboot-1.1.1-SNAPSHOT.jar --server.port=8082 ![image-20220127235131560](Markdown_Yong_JPG/image-20220127235131560.png) ### 3个都启动起来 ![image-20220127235311029](Markdown_Yong_JPG/image-20220127235311029.png) ## 浏览器访问 > 服务器端 默认用户名user 默认密码user > > http://xxlssoserver.com:8080/xxl-sso-server/login > > 客户端1 > > client1.com:8081/xxl-sso-web-sample-springboot > > 客户端2 > > client2.com:8082/xxl-sso-web-sample-springboot ## 登陆效果 - 一处登陆,都能访问 - 一处退出,全部退出 # 4.idea中使用单点登录 ## 1.单点登录使用的模块文件 ![image-20220128171830353](Markdown_Yong_JPG/image-20220128171830353.png) ## 2.一些配置信息 ``` #服务器地址 http://ssoserver.com:8080/login.html #员工信息页 client1.com:8081/employees #boose信息页 client2.com:8082/boss ``` ## 3.单点登录流程 ![单点登录流程](Markdown_Yong_JPG/单点登录流程.png) ## 4.一些文件配置信息 ### 4.1 service服务端 localhost:8080的配置 #### 1.application配置 ```properties #配置端口号 server.port=8080 #redis缓存地址,密码,端口 spring.redis.host=127.0.0.1 spring.redis.password=******** spring.redis.port=6379 #设置日志打印级别 logging.level.com.atguigu.gulimall.ssoserver.controller=debug ``` #### 2.controller控制器文件 ```java package com.atguigu.gulimall.ssoserver.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.thymeleaf.util.StringUtils; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; /** * 登陆控制器 * @title: LoginController * @Author cxcc * @Date: 2022/1/28 下午 2:07 */ @Slf4j @Controller public class LoginController { @Autowired StringRedisTemplate redisTemplate; @ResponseBody @GetMapping("/userInfo") public String userInfo(@RequestParam("token") String token){ //从redis中获取token String s = redisTemplate.opsForValue().get(token); return s; } /** * 跳转到登录页 * @param url //获取请求参数 重定向地址 例:http://client1.com:8081/employees * @param model * @param sso_token 非必要参数 * @return */ @GetMapping("/login.html") public String loginPage(@RequestParam("redirect_url") String url, Model model, @CookieValue(value = "sso_token",required = false)String sso_token){ log.debug("跳转到登录页前的重定向地址是:{}",url); //判断sso_token是否为空,不为空则说明用户已经在别处登陆,可以直接跳转到登录前页面 if(!StringUtils.isEmpty(sso_token)){ return "redirect:"+url+"?token="+sso_token; } //将重定向地址放入model,用于返回前端 model.addAttribute("url",url); return "login";//跳转到登录页 } /** * 跳转回登陆前页面 * @param username 用户名 * @param password 密码 * @param url 重定向地址 * @return */ @PostMapping("/doLogin") public String doLogin(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("url") String url, HttpServletResponse response){ //登陆成功,跳转到之前的页面 if(!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)){ log.debug("登录成功,登陆后要跳转的重定向地址是:{}",url); //将登陆成功的用户存入redis String uuid = UUID.randomUUID().toString().replace("-",""); redisTemplate.opsForValue().set(uuid,username); //创建cookie Cookie sso_token = new Cookie("sso_token", uuid); log.debug("sso_token的值是{}",sso_token); //存储cookie response.addCookie(sso_token); //重定向到登录页 return "redirect:"+url+"?token="+uuid; } //登陆失败,还是跳转登陆页 return "login"; } } ``` #### 3.login.html登陆文件 ```html 登陆页
用户名:
密码:

``` ### 4.2 client1客户端:8081的配置 #### 1.application的配置 ```properties #设置端口号 server.port=8081 #登陆服务器端的地址 sso.server.url=http://ssoserver.com:8080/login.html #设置日志打印级别 logging.level.com.atguige.gulimall.ssoclient.controller=debug ``` #### 2.controller控制器文件 ```java package com.atguige.gulimall.ssoclient.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import org.thymeleaf.util.StringUtils; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.List; /** * * @title: HelloController * @Author cxcc * @Date: 2022/1/28 下午 1:25 */ @Slf4j @Controller //控制器类 public class HelloController { @Value("${sso.server.url}")//获取配置文件中的数据 String ssoServerUrl; /** * 无需登陆就能访问 * @return */ @ResponseBody @GetMapping("/hello") public String hello(){ return "hello"; } /** * 只有登陆才能访问 * @param model * @param session 作用域 用于判断用户是否登陆 * @param token 不是必须参数,只要是去ssoserver登陆成功跳转回来,就会带上 * @return */ @GetMapping("/employees") public String employees(Model model, HttpSession session, @RequestParam(value = "token",required = false)String token){ log.debug("token={}",token); //判断,如果token不为空,则说明是从登陆页跳转回来的 if(!StringUtils.isEmpty(token)){ //说明登录成功 //TODO 远程去ssoserver获取当前token对应的用户信息 RestTemplate restTemplate = new RestTemplate(); ResponseEntity forEntity = restTemplate.getForEntity("http://ssoserver.com:8080/userInfo?token=" + token, String.class); log.debug("forEntity=>{}",forEntity); String body = forEntity.getBody(); session.setAttribute("loginUser",body); } //获取session Object loginUser = session.getAttribute("loginUser"); log.debug("session是否为空loginUser:{}",loginUser); //判断session是否存在 if(loginUser==null){ //没登录 跳转登陆服务器进行登录 //重定向到登录地址,并加入自己的地址作为参数 //例:http://ssoserver.com:8080/login.html?redirect_url=http://client1.com:8081/employees return "redirect:"+ssoServerUrl+"?redirect_url=http://client1.com:8081/employees"; }else{ // 存在则说明已经登陆,可以获取登陆后的数据 List emps = new ArrayList<>(); emps.add("张三"); emps.add("李四"); //将用户信息放入model model.addAttribute("emps",emps); return "list"; //跳转到list.html页面 } } } ``` #### 3.list.html文件信息 ```html 员工列表

欢迎:[[${session.loginUser}]]

``` ### 4.3 client2客户端:8082的配置 #### 1.application的配置 ```properties #设置端口号 server.port=8082 #登陆服务器端的地址 sso.server.url=http://ssoserver.com:8080/login.html #设置日志打印级别 logging.level.com.atguige.gulimall.ssoclient.controller=debug ``` #### 2.controller控制器的配置 ```java package com.atguige.gulimall.ssoclient.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import org.thymeleaf.util.StringUtils; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.List; /** * * @title: HelloController * @Author cxcc * @Date: 2022/1/28 下午 1:25 */ @Slf4j @Controller //控制器类 public class HelloController { @Value("${sso.server.url}")//获取配置文件中的数据 String ssoServerUrl; /** * 无需登陆就能访问 * @return */ @ResponseBody @GetMapping("/hello") public String hello(){ return "hello"; } /** * 只有登陆才能访问 * @param model * @param session 作用域 用于判断用户是否登陆 * @param token 不是必须参数,只要是去ssoserver登陆成功跳转回来,就会带上 * @return */ @GetMapping("/boss") public String employees(Model model, HttpSession session, @RequestParam(value = "token",required = false)String token){ log.debug("token={}",token); //判断,如果token不为空,则说明是从登陆页跳转回来的 if(!StringUtils.isEmpty(token)){ //说明登录成功 //TODO 远程去ssoserver获取当前token对应的用户信息 RestTemplate restTemplate = new RestTemplate(); ResponseEntity forEntity = restTemplate.getForEntity("http://ssoserver.com:8080/userInfo?token=" + token, String.class); log.debug("forEntity=>{}",forEntity); String body = forEntity.getBody(); session.setAttribute("loginUser",body); } //获取session Object loginUser = session.getAttribute("loginUser"); log.debug("session是否为空loginUser:{}",loginUser); //判断session是否存在 if(loginUser==null){ //没登录 跳转登陆服务器进行登录 //重定向到登录地址,并加入自己的地址作为参数 //例:http://ssoserver.com:8080/login.html?redirect_url=http://client2.com:8082/bost return "redirect:"+ssoServerUrl+"?redirect_url=http://client2.com:8082/boss"; }else{ // 存在则说明已经登陆,可以获取登陆后的数据 List emps = new ArrayList<>(); emps.add("张三"); emps.add("李四"); //将用户信息放入model model.addAttribute("emps",emps); return "list"; //跳转到list.html页面 } } } ``` #### 3.list.html文件信息 ```html 员工列表

欢迎:[[${session.loginUser}]]

  • 姓名:[[${emp}]]
``` # 5.idea使用单点登录演示 ## 0.配置系统host ``` # 单点登陆 127.0.0.1 xxlssoserver.com 127.0.0.1 ssoserver.com 127.0.0.1 client1.com 127.0.0.1 client2.com ``` ## 1.先启动这三个模块 ![image-20220517225227188](Markdown_Yong_JPG/image-20220517225227188.png) ## 2.查看本地redis为空说明没有用户登录 ![image-20220517225400333](Markdown_Yong_JPG/image-20220517225400333.png) ## 3.直接访问http://client1.com:8080/login.html的登陆页 发现访问不了,说需要参数 ![image-20220517225753719](Markdown_Yong_JPG/image-20220517225753719.png) 此时查看redis,发现里边空白 ![image-20220517225927206](Markdown_Yong_JPG/image-20220517225927206.png) ## 4.访问http://client1.com:8081/hello页面 发现能访问成功,因为该页面没有登陆限制,所以可以访问 ![image-20220517230315266](Markdown_Yong_JPG/image-20220517230315266.png) ## 5.访问http://client1.com:8082/hello页面 发现能访问成功,因为该页面没有登陆限制,所以可以访问 ![image-20220517230502698](Markdown_Yong_JPG/image-20220517230502698.png) ## 6.现在我们在http://client1.com:8081/访问employees ``` 访问http://client1.com:8081/employees,然后点击回车,会发现重定向到了8080端口的登陆地址,同时在登陆地址后边,携带了一个地址, 这个携带的地址是我们在8081端口要访问的地址 ``` ![image-20220517230828613](Markdown_Yong_JPG/image-20220517230828613.png) ![image-20220517231300524](Markdown_Yong_JPG/image-20220517231300524.png) ## 7.现在我们输入用户名和密码,然后点击登陆 ![image-20220517231543473](Markdown_Yong_JPG/image-20220517231543473.png) ``` 我们会发现,页面重新重定向到了我们带8081端口上访问的地址 同时地址上还携带了一个token 也显示了页面信息 ``` ![image-20220517231639242](Markdown_Yong_JPG/image-20220517231639242.png) ``` 同时,我们查看redis,发现redis中存储了我们刚才页面展示的token 键f37144d610d643d5920a36875ae12032 值123456是用户名 ``` ![image-20220517231958431](Markdown_Yong_JPG/image-20220517231958431.png) ## 8.现在我们访问http://client1.com:8082/boss地址 ![image-20220517232243787](Markdown_Yong_JPG/image-20220517232243787.png) ``` 单击回车,发现也显示了登陆的信息,这说明我们在8081端口访问登陆服务器登陆之后,在8082端口同时也是登录状态,不用重新登陆,这个就是单点登录,一处登陆,处处登录 ``` ![image-20220517232309780](Markdown_Yong_JPG/image-20220517232309780.png)