java实现单点登陆(SSO)

article/2025/9/3 19:51:47

java实现单点登陆(SSO)

网络域名必须完全一致,才代表同一站点。
域名映射 :访问后面的 会跳转到前面

单点登陆概念:
多系统,单一位置登录,实现多系统同时登陆。常出现在互联网和企业级平台中。
单点登陆一般是用于互相授信的系统,实现单一位置登录,全系统有效。
三方登录:某系统使用其他系统的用户,实现本系统登录的方式,如在jd上使用wx登录。解决信息孤岛(共享用户的名字,而不是密码,用户在每个系统都有自己的密码)和用户不对等的实现方案。

登录方式:
session跨域

摒弃系统(tomcat)提供的session,使用自定义的类似session的机制来保存客户端数据的一种解决方案。
如:通过设置cookie的domain实现cookie的跨域传递,在cookie中传递一个自定义的session id,这个session id 是客户端的,将这个标记作为key,将客户端需要保存的数据作为value,在服务端进行保存(数据库保存或者在nosql保存)。

什么是跨域:客户端请求的时候,请求的服务器,不是同一个IP、端口、域名、主机名的时候,都称为跨域。

什么是域:在应用模型,一个完整的,有独立访问路径的功能集合称为一个域,如:百度称为一个应用系统,百度下有若干域,搜索引擎(www.baidu.com)百度贴吧(tieba.baidu.com)百度知道(zhidao.baidu.com)。域信息、有时也称为多级域名。域的划分,以IP、端口、域名、主机名

整个项目采用springboot+maven,只实现了单点登陆而已哦~
maven包(放在父工程即可):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"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"><modelVersion>4.0.0</modelVersion><groupId>com.sso</groupId><artifactId>sso-use-cookie</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>sso-main</module><module>sso-vip</module><module>sso-cart</module><module>sso-login</module></modules><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.4.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId><version>2.2.4.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.2.4.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.6</version></dependency></dependencies></project>

项目整体:
在这里插入图片描述
登录模块(port=9000):
在这里插入图片描述
LoginController:

package com.sso.login.controller;import com.sso.login.poho.User;
import com.sso.login.utils.LoginCacheUtil;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;@Controller
@RequestMapping("/login")
public class LoginController {private static Set<User> dbUsers;static {dbUsers = new HashSet<>();dbUsers.add(new User(0,"zhangsan","123456"));dbUsers.add(new User(1,"lisi","123456"));dbUsers.add(new User(0,"wangwu","123456"));}@PostMappingpublic String doLogin(User user, HttpSession session, HttpServletResponse response){String target = (String) session.getAttribute("target");//模拟从数据库中通过登陆的用户名和密码去查找数据库中用户Optional<User> first = dbUsers.stream().filter(dbUser -> dbUser.getUsername().equals(user.getUsername()) &&dbUser.getPassword().equals(user.getPassword())).findFirst();//判断用户是否登陆if(first.isPresent()){//保存用户的登录信息String token = UUID.randomUUID().toString();Cookie cookie = new Cookie("TOKEN",token);cookie.setDomain("codeshop.com");response.addCookie(cookie);LoginCacheUtil.loginUser.put(token,first.get());}else{//登陆失败session.setAttribute("msg","用户名或密码错误");return "login";}//重定向到target地址return "redirect:" + target;}@GetMapping("info")@ResponseBodypublic ResponseEntity<User> getUserInfo(String token){if(!StringUtils.isEmpty(token)){User user = LoginCacheUtil.loginUser.get(token);return ResponseEntity.ok(user);}else {return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);}}
}

ViewController:

package com.sso.login.controller;import com.sso.login.poho.User;
import com.sso.login.utils.LoginCacheUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;/*** 页面跳转逻辑*/
@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)){target = "http://www.codeshop.com:9010";}//如果是已经登陆的用户再次访问登录页面,需要重定向if(cookie != null){//tokenString value = cookie.getValue();User user = LoginCacheUtil.loginUser.get(value);if(user != null){return "redirect:" + target;}}//TODO:要做target地址是否合法的校验//重定向地址session.setAttribute("target",target);return "login";}
}

User:

package com.sso.login.poho;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;@Data//添加getter/setter
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)//添加链式调用
public class User {private Integer id;private String username;private String password;
}

LoginCacheUtil:

package com.sso.login.utils;import com.sso.login.poho.User;import java.util.HashMap;
import java.util.Map;public class LoginCacheUtil {public static Map<String , User> loginUser = new HashMap<>();
}

LoginApp :

package com.sso.login;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class LoginApp {public static void main(String[] args) {SpringApplication.run(LoginApp.class,args);}
}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8"/><title>Login Module</title>
</head>
<body><h1>欢迎来到登录页面</h1><p style="color: red;" th:text="${session.msg}"></p><form action="/login" method="POST">用户名:<input name="username" value=""/>密码:<input name="password" value=""/><button type="submit">登录</button></form>
</body>
</html>

aoolication.yml:

server:port: 9000

主页模块(port=9010)
在这里插入图片描述
ViewController :

package com.sso.main.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.util.Map;@Controller
@RequestMapping("/view")
public class ViewController {@Autowiredprivate RestTemplate restTemplate;private final String LOGIN_INFO_ADDRESS = "http://login.codeshop.com:9000/login/info?token=";@GetMapping("/index")public String toIndex(@CookieValue(required = false,value = "TOKEN")Cookie cookie, HttpSession session){if(cookie != null){String token = cookie.getValue();if(!StringUtils.isEmpty(token)){Map result =  restTemplate.getForObject(LOGIN_INFO_ADDRESS + token, Map.class);session.setAttribute("loginUser",result);}}return "index";}}

MainApp :

package com.sso.main;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;@SpringBootApplication
public class MainApp {public static void main(String[] args) {SpringApplication.run(MainApp.class,args);}@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:http="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body><h1>欢迎来到Code Shop</h1><span><a th:if="${session.loginUser == null}" href="http://login.codeshop.com:9000/view/login?target=http://www.codeshop.com:9010/view/index">登录</a><a th:unless="${session.loginUser == null}" href="#">退出</a></span><p th:unless="${session.loginUser == null}"><span style="color: deepskyblue;" th:text="${session.loginUser.username}"></span>已登录</p>
</body>
</html>

application.yml:

server:port: 9010

vip模块(port=9011)
在这里插入图片描述
ViewController :

package com.oss.vip.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.util.Map;@Controller
@RequestMapping("/view")
public class ViewController {@Autowiredprivate RestTemplate restTemplate;private final String USER_INFO_ADDRESS = "http://login.codeshop.com:9000/login/info?token=";@GetMapping("/index")public String toIndex(@CookieValue(required = false,value = "TOKEN")Cookie cookie,HttpSession session){if(cookie != null){String token = cookie.getValue();if(!StringUtils.isEmpty(token)){Map result = restTemplate.getForObject(USER_INFO_ADDRESS + token, Map.class);session.setAttribute("loginUser",result);}}return "index";}
}

VipApp :

package com.oss.vip;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;@SpringBootApplication
public class VipApp {public static void main(String[] args) {SpringApplication.run(VipApp.class,args);}@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8"><title>Vip</title>
</head>
<body>
<h1>欢迎来到Vip系统</h1>
<span><a th:if="${session.loginUser == null}" href="http://login.codeshop.com:9000/view/login?target=http://vip.codeshop.com:9011/view/login">登录</a><a th:unless="${session.loginUser == null}" href="#">退出</a></span>
<p th:unless="${session.loginUser == null}"><span style="color: deepskyblue;" th:text="${session.loginUser.username}"></span>已登录</p>
</body>
</html>

application.yml:

server:port: 9011

购物车模块(port=9012)
在这里插入图片描述
ViewController :

package com.oss.cart.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.util.Map;@Controller
@RequestMapping("/view")
public class ViewController {@Autowiredprivate RestTemplate restTemplate;private final String USER_INFO_ADDRESS = "http://login.codeshop.com:9000/login/info?token=";@GetMapping("/index")public String toIndex(@CookieValue(required = false,value = "TOKEN") Cookie cookie,HttpSession session){if(cookie != null){String token = cookie.getValue();if(!StringUtils.isEmpty(token)){Map result = restTemplate.getForObject(USER_INFO_ADDRESS + token,Map.class);session.setAttribute("loginUser",result);}}return "index";}
}

CartApp :

package com.oss.cart;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;@SpringBootApplication
public class CartApp {public static void main(String[] args) {SpringApplication.run(CartApp.class,args);}@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8"><title>Cart</title>
</head>
<body>
<h1>欢迎来到Cart页面</h1>
<span><a th:if="${session.loginUser == null}" href="http://login.codeshop.com:9000/view/login?target=http://cart.codeshop.com:9012/view/login">登录</a><a th:unless="${session.loginUser == null}" href="#">退出</a></span>
<p th:unless="${session.loginUser == null}"><span style="color: deepskyblue;" th:text="${session.loginUser.username}"></span>已登录</p>
</body>
</html>

application.yml:

server:port: 9012

http://chatgpt.dhexx.cn/article/8Q5opqrc.shtml

相关文章

OAuth2:单点登陆客户端

基于EnableOAuth2Sso实现 前面我们将验证服务器已经搭建完成了&#xff0c;现在我们就来实现一下单点登陆吧&#xff0c;SpringCloud为我们提供了客户端的直接实现&#xff0c;我们只需要添加一个注解和少量配置即可将我们的服务作为一个单点登陆应用&#xff0c;使用的是第四种…

单点登陆概述

概述 什么是单点登陆 单点登陆&#xff08;single sign on&#xff09;&#xff0c;简称SSO&#xff0c;是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中&#xff0c;用户只需要登陆一次就可以访问所以相互信任的应用系统。 单点登陆的实现方案 一般…

怎么做登陆(单点登陆)功能?

登陆是系统最基础的功能之一。这么长时间了&#xff0c;一直在写业务&#xff0c;这个基础功能反而没怎么好好研究&#xff0c;都忘差不多了。今天没事儿就来撸一下。 以目前在接触的一个系统为例&#xff0c;来分析一下登陆该怎么做。 简单上个图&#xff08;有水印。因为穷所…

SSO单点登陆

1 SSO简介 1.1 什么是SSO 单点登录(SingleSignOn&#xff0c;SSO)&#xff0c;在多个应用系统中&#xff0c;用户只需一次登录就可以访问所有相互信任的应用系统。单点登录常用的协议包括 CAS、OAuth、OpenID Connect、SAML。 例如&#xff1a;百度旗下有很多的产品&#xff…

sso单点登陆实现

多系统实现单点登录方案&#xff1a;SSO 单点登录 一、什么是单点登录SSO&#xff08;Single Sign-On&#xff09; SSO是一种统一认证和授权机制&#xff0c;指访问同一服务器不同应用中的受保护资源的同一用户&#xff0c;只需要登录一次&#xff0c;即通过一个应用中的安全验…

单点登陆和无状态登陆

很多人都听说过单点登陆。今天我们来说说什么是单点登陆和无状态登陆。 传统的项目都是使用session来验证登陆&#xff0c;但是在分布式项目中使用session是不行的。因为每个服务都是一个独立的项目&#xff0c;那么我们将服务拆分&#xff0c;肯定会有一个登陆的模块。如果将用…

windows文件句柄修改

找到如下注册表分支&#xff1a; HKEY_LOCAL_MACHINE – SOFTWARE – – Microsoft – – – Windows NT – – – – CurrentVersion – – – – – Windows 在右侧窗格中可以看到名为“GDIProcessHandleQuota”与“USERProcessHandleQuota”的注册表项; GDIProcessHandleQuo…

Linux 查看文件句柄信息

查看系统的最大文件句柄数和文件句柄的使用者PID ulimit -n查看当前系统的最大句柄数显示如下 ulimit命令详解 ulimit -HSn x设置当前系统的文件句柄数为x 以上命令中&#xff0c;H指定了硬性大小&#xff0c;S指定了软性大小&#xff0c;n表示设定单个进程最大的打开文件句柄…

Windows查看文件句柄

2019独角兽企业重金招聘Python工程师标准>>> 图形界面方式 打开任务管理器 2. 性能tab,点击链接打开资源监视器; 3. 现在cpu tab,关联的句柄后面的输入框可以输入你要搜索的文件路径,可模糊匹配; 命令方式 Windows系统本身并不内置命令查看句…

linux文件句柄数

1、问题阐述&#xff1a; too many open files&#xff1a;顾名思义即打开过多文件数。 不过这里的files不单是文件的意思&#xff0c;也包括打开的通讯链接(比如socket)&#xff0c;正在监听的端口等等&#xff0c;所以有时候也可以叫做句柄(handle)&#xff0c;这个错误通常…

windows 查看打开的文件句柄

经常当我们删除文件时&#xff0c;有时会提示【操作无法完成&#xff0c;因为文件已在另一个程序中打开&#xff0c;请关闭该文件并重试】&#xff0c; 这个时候可以资源监控器进行查看运行的进程打开的句柄列表。 具体结果如下显示&#xff1a;

Linux下文件句柄

Too many open files 如果Java打开文件的时候&#xff0c;没有关闭IO流&#xff0c;那么打开到一定数量&#xff0c;在Linux下就会抛出Too many open files的异常。 public static class HoldIOTask implements Runnable {Overridepublic void run() {int count0;try {while(t…

【Linux】文件句柄说明

Linux 文件句柄说明 Linux中所有的事物或资源都是以文件的形式存在&#xff0c;比如消息、共享内存、连接等&#xff0c;句柄可以理解为指向这些文件的指针。 对于这些句柄&#xff0c;Linux是有数量限制的&#xff0c;单个进程默认可以打开的句柄数上限&#xff0c;可以用以…

windows 文件句柄查询

windows经常出现删除/剪切文件时提示文件被占用的问题 打开任务管理器&#xff0c; 选择“性能”选项卡 2. 打开资源管理器&#xff0c; 在关联句柄查询框中输入关键字&#xff0c;过滤查询&#xff0c;然后右键选中对应项&#xff0c;结束任务即可释放句柄

文件句柄

一、文件句柄是什么&#xff1f; 对象在内存中是经常来回移动的&#xff0c;如何快速定位找到这个对象呢&#xff1f; 句柄说&#xff1a; “我来帮你找” windows系统给出的方案 1.进程创建时&#xff0c;windows系统为进程构造了一个句柄表 2.当该进程希望获得一个内核对…

linux开关机命令

shutdown -h now/或者是具体的时间hh:mm&#xff1b; 关机; -h:关机。&#xff1b;-r&#xff1a; 重启 &#xff1b;-c&#xff1a; 取消前一个关机命令。一般就只用shutdown这个命令&#xff1b;halt&#xff0c;poweroff&#xff0c;init 0 关机&#xff1b;init 6&#xff…

Linux命令之关机命令

shutdown命令可以用来进行关闭系统&#xff0c;并且在关机以前传送讯息给所有使用者正在执行的程序&#xff0c;shutdown 也可以用来重开机使用者权限&#xff1a;管理员用户 语法如下&#xff1a; shutdown [-t seconds] [-rkhncfF] time [message] 参数说明&#xff1a; …

Linux 关机 命令

一、正确的关机方法 如果直接按下电源开关&#xff0c;数据有可能中断。若不正常关机&#xff0c;则有可能造成文件系统的损毁。因此&#xff0c;正常关机情况下&#xff0c;应注意以下&#xff1a; 1&#xff09;查看系统的使用状态 查看目前有谁在线&#xff0c;使用“who…

部署图详解

部署图 一、部署图概要 ​ 部署图用于静态建模&#xff0c;是表示运行时过程节点结构、构件实例及其对象结构的图。如果含有依赖关系的构件实例放置在不同节点上&#xff0c;部署视图可以展示出执行过程中的瓶颈。部署图的两种表现形式&#xff1a;实例层部署图和描述层部署图…

EA_画部署图

EA_画部署图 新建部署图增加个性化图标画一个范围Boundary 新建部署图 右键–>添加图 增加个性化图标 右键图标–>外观–>选择可替代图像 选择自定义的图标 画一个范围Boundary