# 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的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
```
一处登陆,处处登录
```

# 2.单点登录框架介绍
https://gitee.com/xuxueli0323/xxl-sso?_from=gitee_search
# 3.单点登录框架的使用
## 单点登录服务器端启动
### 1.克隆下来这个仓库,修改配置文件

> **每个子项目的配置文件都要修改**

### 2.修改系统host

### 3.将克隆下来的项目打包
```
mvn clean package -Dmaven.skip.test=true
```

### 4.启动服务
```
java -jar xxl-sso-server-1.1.1-SNAPSHOT.jar
```

### 5.浏览器登录进去

```
http://xxlssoserver.com:8080/xxl-sso-server/login
```

## 单点登录客户端启动
### 1.启动
> java -jar xxl-sso-web-sample-springboot-1.1.1-SNAPSHOT.jar --server.port=8081

#### 启动两个客户端,端口号让不一样
> java -jar xxl-sso-web-sample-springboot-1.1.1-SNAPSHOT.jar --server.port=8082

### 3个都启动起来

## 浏览器访问
> 服务器端 默认用户名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.单点登录使用的模块文件

## 2.一些配置信息
```
#服务器地址
http://ssoserver.com:8080/login.html
#员工信息页
client1.com:8081/employees
#boose信息页
client2.com:8082/boss
```
## 3.单点登录流程

## 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}]]
```
# 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.先启动这三个模块

## 2.查看本地redis为空说明没有用户登录

## 3.直接访问http://client1.com:8080/login.html的登陆页
发现访问不了,说需要参数

此时查看redis,发现里边空白

## 4.访问http://client1.com:8081/hello页面
发现能访问成功,因为该页面没有登陆限制,所以可以访问

## 5.访问http://client1.com:8082/hello页面
发现能访问成功,因为该页面没有登陆限制,所以可以访问

## 6.现在我们在http://client1.com:8081/访问employees
```
访问http://client1.com:8081/employees,然后点击回车,会发现重定向到了8080端口的登陆地址,同时在登陆地址后边,携带了一个地址,
这个携带的地址是我们在8081端口要访问的地址
```


## 7.现在我们输入用户名和密码,然后点击登陆

```
我们会发现,页面重新重定向到了我们带8081端口上访问的地址
同时地址上还携带了一个token
也显示了页面信息
```

```
同时,我们查看redis,发现redis中存储了我们刚才页面展示的token
键f37144d610d643d5920a36875ae12032
值123456是用户名
```

## 8.现在我们访问http://client1.com:8082/boss地址

```
单击回车,发现也显示了登陆的信息,这说明我们在8081端口访问登陆服务器登陆之后,在8082端口同时也是登录状态,不用重新登陆,这个就是单点登录,一处登陆,处处登录
```
