SSO(单点登录)
Single Sign On 一处登录、处处可用
单点登录
一、 单点登录业务介绍
早期单一服务器,用户认证
缺点:单点性能压力,无法扩展
WEB应用集群,session共享模式
解决了单点性能瓶颈。
问题:
1、 多业务分布式数据独立管理,不适合统一维护一份session数据。
2、 分布式按业务功能切分,用户、认证解耦出来单独统一管理。
3、 cookie中使用jsessionId 容易被篡改、盗取。
4、 跨顶级域名无法访问。
分布式,SSO(single sign on)模式
解决 :
用户身份信息独立管理,更好的分布式管理;可以自己扩展安全策略;跨域不是问题
缺点:认证服务器访问压力较大。
业务流程图
二、示例
1、分布式单点登录框架XXL-SSO
2、自行搭建
创建springboot
项目 gulimall-sso-server
和 gulimall-sso-client
gulimall-sso-server
服务
登录服务器
1、引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、application.yaml配置
server:
port: 8080
#redis配置
redis:
host: xxx
port: 6379
3、resource下添加login页面
<!DOCTYPE html>
<html lang="en">
<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="redirect_url" value="http://localhost:8081/employees" />
<input type="submit" value="登录">
</form>
</body>
</html>
4、LoginController
package com.xunqi.gulimall.ssoserver.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;
@Controller
public class LoginController {
@Autowired
StringRedisTemplate redisTemplate;
/**
* 其它客户端登录成功,会调这个接口获取用户信息
*/
@ResponseBody
@GetMapping("/userinfo")
public String userinfo(@RequestParam(value = "token") String token) {
String s = redisTemplate.opsForValue().get(token);
return s;
}
/**
如果携带了cookie,登录成功
*/
@GetMapping("/login.html")
public String loginPage(@RequestParam("redirect_url") String url,
Model model,
@CookieValue(value = "sso_token", required = false) String sso_token) {
if (!StringUtils.isEmpty(sso_token)) {
return "redirect:" + url + "?token=" + sso_token;
}
//前端页面数据需要
model.addAttribute("url", url);
//跳到登录页
return "login";
}
/**
登录
redirect_url:重定向到客户端的url
*/
@PostMapping(value = "/doLogin")
public String doLogin(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("redirect_url") String url,
HttpServletResponse response) {
//登录成功,携带token 跳回到客户端页面
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
String uuid = UUID.randomUUID().toString().replace("_", "");
redisTemplate.opsForValue().set(uuid, username);
Cookie sso_token = new Cookie("sso_token", uuid);
response.addCookie(sso_token);
return "redirect:" + url + "?token=" + uuid;
}
return "login";
}
}
gulimall-sso-client
package com.xunqi.gulimall.ssoclient.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.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
@Controller
public class HelloController {
@GetMapping(value = "/employees")
public String employees(Model model,
HttpSession session,
@RequestParam(value = "token", required = false) String token) {
if (!StringUtils.isEmpty(token)) {
//登录成功
RestTemplate restTemplate=new RestTemplate();
//获取用户信息
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://sso.mroldx.cn:8080/userinfo?token=" + token, String.class);
String body = forEntity.getBody();
session.setAttribute("loginUser", body);
}
Object loginUser = session.getAttribute("loginUser");
if (loginUser == null) {
//session里没登录信息,跳到登录服务器,携带参数redirect_url
return "redirect:" + "http://sso.mroldx.cn:8080/login.html"+"?redirect_url=http://localhost:8081/employees";
} else {
List<String> emps = new ArrayList<>();
emps.add("张三");
emps.add("李四");
model.addAttribute("emps", emps);
return "employees";
}
}
}