SSO单点登录-cookie实现

SSO单点登录-cookie实现

单点登录及实现方案原理

单点登录概念:

​ 单点登录英文全称Single Sign On,简称就是SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。例如,网页登录了淘宝账号,天猫,钉钉等阿里系应用都不用再二次登录了。 SSO核心意义就一句话:一处登录,处处登录;一处注销,处处注销。

一、基于cookie实现单点登录

这是最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。 用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie并进行校验,校验通过则登录当前用户。

缺点:

Cookie不安全不能跨域实现免登

对于第一个问题,通过加密Cookie可以保证安全性,当然这是在源代码不泄露的前提下。如果Cookie 的加密算法泄露,攻击者通过伪造Cookie则可以伪造特定用户身份,这是很危险的;对于第二个问题,不能跨域实现免登更是硬伤。因此,有了基于Session的单点登录。

cookie实现单点登录流程分析

需要做ip和域名映射:

#主系统

127.0.0.1 www.sso.com

#登录系统

127.0.0.1 login.sso.com

#vip子系统

127.0.0.1 vip.sso.com

#购物车子系统

127.0.0.1 cart.sso.com

用户首次登录,携带请求页url,跳转登录系统控制层接口

@Controller

@RequestMapping("view")

public class ViewController {

@Autowired

RestTemplate restTemplate;

//携带token跨服务请求登录服务接口获取已经登陆用户信息--若解决session共享则可以省略该步骤

private final String LOGIN_INFO_ADDRESS="http://login.sso.com:9001/login/info?token=";

@GetMapping("/index")

public String toIndex(@CookieValue(required = false,value = "TOKEN")Cookie cookie, HttpSession session){

if(null!=cookie){

String token = cookie.getValue();

if(StringUtils.isNotEmpty(token)){

Map result = restTemplate.getForObject(LOGIN_INFO_ADDRESS + token, Map.class);

session.setAttribute("loginUser",result);

}

}else {

session.removeAttribute("loginUser");

}

return "index";

}

首页

首页

这里是首页展示页面

登录

退出

已登录

判断cookie为空,直接跳转登录界面

/**

* 页面跳转逻辑

*/

@Controller

@RequestMapping("view")

public class ViewController {

/**

* 跳转到登录页面

*

* @return

*/

@GetMapping("/login")

public String toLogin(@RequestParam(required = false, defaultValue = "") String target, HttpSession session,

@CookieValue(required = false, value = "TOKEN") Cookie cookie) {

if (StringUtils.isEmpty(target)) {

//未携带url设置默认页面

target = "http://www.sso.com:9000";

}

if (cookie != null) {

//如果是已经登录的用户再次访问登录系统时,就重定向到回页面

String token = cookie.getValue();

User user = LoginCacheUtil.loginUser.get(token);

if (user != null) {

return "redirect:" + target;

}

}

//未登录,跳转登录controller

session.setAttribute("target", target);

return "login";

}

}

登录界面

Login Module

欢迎来到登录页面

用户名:

密码:

登录页面表单提交,登录控制层接收用户信息并校验数据是否正确,用户存在,则将该用户信息保存(redis,或Map),创建cookie,将新建token存储在cookie中,并响应到HttpServletResponse中供其他系统访问,登录成功跳回请求系统控制层接口。

判断是否登录,已登录判断用户存在session,跳转会请求页面

@Controller

@RequestMapping("view")

public class ViewController {

/**

* 跳转到登录页面

*

* @return

*/

@GetMapping("/login")

public String toLogin(@RequestParam(required = false, defaultValue = "") String target, HttpSession session,

@CookieValue(required = false, value = "TOKEN") Cookie cookie) {

if (StringUtils.isEmpty(target)) {

//未携带url设置默认页面

target = "http://www.sso.com:9000";

}

if (cookie != null) {

//如果是已经登录的用户再次访问登录系统时,就重定向到回页面

String token = cookie.getValue();

User user = LoginCacheUtil.loginUser.get(token);

if (user != null) {

return "redirect:" + target;

}

}

//未登录,跳转登录controller

session.setAttribute("target", target);

return "login";

}

}

未登录请求登录接口

@Controller

@RequestMapping("/login")

public class LoginController {

// 模拟数据库用户

private static Set dbUser;

static {

dbUser = new HashSet<>();

dbUser.add(new User(0, "zhangsan", "1234"));

dbUser.add(new User(1, "lisi", "1234"));

dbUser.add(new User(2, "中国魂", "123456"));

}

@PostMapping

public String doLogin(User user, HttpSession session, HttpServletResponse response) {

String target = (String) session.getAttribute("target");

// 模拟从数据库中通过登录的用户名和密码查找用户,

Optional first = dbUser.stream().filter(dbUser -> dbUser.getUsername().equals(user.getUsername()) &&

dbUser.getPassword().equals(user.getPassword())).findFirst();

// 判断用户是否登录d

if (first.isPresent()) {

// 保存用户登录信息

String token = UUID.randomUUID().toString();

LoginCacheUtil.loginUser.put(token, first.get());

session.removeAttribute("msg");

//设置cookie

Cookie cookie = new Cookie("TOKEN", token);

//cookie在子系统间访问域要相同

//cookie不能跨域

cookie.setDomain("sso.com");

/*

cookie过期时间设置方式:

cookie.setMaxAge(0);//不记录cookie

cookie.setMaxAge(-1);//会话级cookie,关闭浏览器失效

cookie.setMaxAge(60*60);//过期时间为1小时

*/

cookie.setMaxAge(-1);

cookie.setPath("/");

//通过HttpServletResponse将cookie响应到子系统

response.addCookie(cookie);

} else {

//登录失败

session.setAttribute("msg", "用户名或密码错误");

return "login";

}

// 重定向到target地址

return "redirect:" + target;

}

(用户回显)判断cookie不为空,从cookie中获取token,用token从redis中或者跨系统从Map中获取用户信息,并存入session,跳转请求页面显示登录用户。

子系统点击登录,在登录系统中判断cookie是否存在,存在则获取token,根据token获取登录用户信息,如果用户存在,则直接跳回请求的子系统页面。

子系统页面

购物车

这里是购物车展示页面

登录

退出

已登录

登录逻辑

@Controller

@RequestMapping("view")

public class ViewController {

@Autowired

RestTemplate restTemplate;

//携带token跨服务请求登录服务接口获取已经登陆用户信息--若解决session共享则可以省略该步骤

private final String LOGIN_INFO_ADDRESS="http://login.sso.com:9001/login/info?token=";

@GetMapping("/index")

public String toIndex(@CookieValue(required = false,value = "TOKEN")Cookie cookie, HttpSession session){

if(null!=cookie){

String token = cookie.getValue();

if(StringUtils.isNotEmpty(token)){

Map result = restTemplate.getForObject(LOGIN_INFO_ADDRESS + token, Map.class);

session.setAttribute("loginUser",result);

}

}else {

session.removeAttribute("loginUser");

}

return "index";

}

跨服务交互接口,从map中获取用户信息

/**

* 通过token获取用户登录信息

* 跨系统交互

*/

@GetMapping("/info")

@ResponseBody

public ResponseEntity getUserInfo(String token) {

if (StringUtils.isNotEmpty(token)) {

User user = LoginCacheUtil.loginUser.get(token);

return ResponseEntity.ok(user);

}

return new ResponseEntity(HttpStatus.BAD_REQUEST);

}

public class LoginCacheUtil {

public static HashMap loginUser = new HashMap<>();

}

退出单点登录:设置cookie过期时间为0,就ok。

//退出

@GetMapping("/exit")

public String toExit(HttpServletRequest request, HttpSession session, HttpServletResponse response) {

Cookie[] cookies = request.getCookies();

if (cookies != null && cookies.length > 0) {

for (Cookie cookie : cookies) {

if ("TOKEN".equals(cookie.getName())) {

cookie.setDomain("sso.com");

cookie.setPath("/");

cookie.setMaxAge(0);

response.addCookie(cookie);

}

}

}

return "redirect:/view/index";

}

pom文件

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.sso

sso-use-cookie

pom

1.0-SNAPSHOT

org.springframework.boot

spring-boot-starter-parent

2.4.1

sso-main

sso-vip

sso-cart

sso-login

UTF-8

UTF-8

1.1

1.8

1.18.16

3.11

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.apache.commons

commons-lang3

${commons-lang3.version}

org.projectlombok

lombok

${lombok.version}

provided

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-maven-plugin

相关推荐

揭秘CSS文件打开变“null”的五大常见原因及解决方案
i7 7700K配什么显卡好(最佳装机配置清单)
www.365bet.com亚洲版

i7 7700K配什么显卡好(最佳装机配置清单)

📅 06-28 ⭐ 3582
2022世界杯突尼斯陣容名單.
www.365bet.com亚洲版

2022世界杯突尼斯陣容名單.

📅 06-30 ⭐ 1389
摩洛哥新球场将成世上最大,争夺2030世界杯决赛主办权
微信好友上限是5000还是10000?微信好友人数限制详解
www.365bet.com亚洲版

微信好友上限是5000还是10000?微信好友人数限制详解

📅 07-05 ⭐ 2740
如何运用excel或spss等软件统计大量纸质问卷?
office365输入账号

如何运用excel或spss等软件统计大量纸质问卷?

📅 07-02 ⭐ 4209
推荐阅读 ❤️