单点登录系统设计思路:采用Spring4 Java配置方式整合HttpClient,Redis ,MySql和SpringBoot的简易教程。

在传统的系统,或者是只有一个服务器的系统中。Session在一个服务器中,各个模块都可以直接获取,只需登录一次就进入各个模块。若在服务器集群或者是分布式系统架构中,每个服务器之间的Session并不是共享的,这会出现每个模块都要登录的情况。这时候需要通过单点登录系统(Single Sign On)将用户信息存在Redis数据库中实现Session共享的效果。从而实现一次登录就可以访问所有相互信任的应用系统。

一、整合 HttpClient

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
首先在src/main/resources 目录下创建 httpclient.properties 配置文件。

#设置整个连接池默认最大连接数
http.defaultMaxPerRoute=100
#设置整个连接池最大连接数
http.maxTotal=300
#设置请求超时
http.connectTimeout=1000
#设置从连接池中获取到连接的最长时间
http.connectionRequestTimeout=500
#设置数据传输的最长时间
http.socketTimeout=10000

然后在 src/main/java/com/itdragon/config 目录下创建 HttpclientSpringConfig.java 文件
这里用到了四个很重要的注解
@Configuration : 作用于类上,指明该类就相当于一个xml配置文件
@Bean : 作用于方法上,指明该方法相当于xml配置中的bean,注意方法名的命名规范
@PropertySource : 指定读取的配置文件,引入多个value={“xxx:xxx”,“xxx:xxx”},ignoreResourceNotFound=true 文件不存在时忽略
@Value : 获取配置文件的值

package com.itdragon.config;
/*** @Configuration    作用于类上,相当于一个xml配置文件* @Bean           作用于方法上,相当于xml配置中的<bean>* @PropertySource  指定读取的配置文件,ignoreResourceNotFound=true 文件不存在是忽略* @Value            获取配置文件的值*/
@Configuration
@PropertySource(value = "classpath:httpclient.properties", ignoreResourceNotFound=true)
public class HttpclientSpringConfig {@Value("${http.maxTotal}")private Integer httpMaxTotal;@Value("${http.defaultMaxPerRoute}")private Integer httpDefaultMaxPerRoute;@Value("${http.connectTimeout}")private Integer httpConnectTimeout;@Value("${http.connectionRequestTimeout}")private Integer httpConnectionRequestTimeout;@Value("${http.socketTimeout}")private Integer httpSocketTimeout;@Autowiredprivate PoolingHttpClientConnectionManager manager;@Beanpublic PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();// 最大连接数poolingHttpClientConnectionManager.setMaxTotal(httpMaxTotal);// 每个主机的最大并发数poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpDefaultMaxPerRoute);return poolingHttpClientConnectionManager;}@Bean // 定期清理无效连接public IdleConnectionEvictor idleConnectionEvictor() {return new IdleConnectionEvictor(manager, 1L, TimeUnit.HOURS);}@Bean  // 定义HttpClient对象 注意该对象需要设置scope="prototype":多例对象@Scope("prototype")public CloseableHttpClient closeableHttpClient() {return HttpClients.custom().setConnectionManager(this.manager).build();}@Bean  // 请求配置public RequestConfig requestConfig() {return RequestConfig.custom().setConnectTimeout(httpConnectTimeout) // 创建连接的最长时间.setConnectionRequestTimeout(httpConnectionRequestTimeout) // 从连接池中获取到连接的最长时间.setSocketTimeout(httpSocketTimeout) // 数据传输的最长时间.build();}}

二、整合 Redis

SpringBoot官方其实提供了spring-boot-starter-redis pom 帮助我们快速开发,但我们也可以自定义配置,这样可以更方便地掌控。
首先在src/main/resources 目录下创建 redis.properties 配置文件

redis.maxTotal=200
redis.node.host=10.128.15.21
redis.node.port=6379
REDIS_USER_SESSION_KEY=REDIS_USER_SESSION
SSO_SESSION_EXPIRE=30

设置Redis主机的ip地址和端口号,和存入Redis数据库中的key以及存活时间。这里为了方便测试,存活时间设置的比较小。这里的配置是单例Redis。
在src/main/java/com/itdragon/config 目录下创建 RedisSpringConfig.java 文件。

@Configuration
@PropertySource(value = "classpath:redis.properties")
public class RedisSpringConfig {@Value("${redis.maxTotal}")private Integer redisMaxTotal;@Value("${redis.node.host}")private String redisNodeHost;@Value("${redis.node.port}")private Integer redisNodePort;private JedisPoolConfig jedisPoolConfig() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(redisMaxTotal);return jedisPoolConfig;}@Bean public JedisPool getJedisPool(){ // 省略第一个参数则是采用 Protocol.DEFAULT_DATABASEJedisPool jedisPool = new JedisPool(jedisPoolConfig(), redisNodeHost, redisNodePort);return jedisPool;}@Beanpublic ShardedJedisPool shardedJedisPool() {List<JedisShardInfo> jedisShardInfos = new ArrayList<JedisShardInfo>();jedisShardInfos.add(new JedisShardInfo(redisNodeHost, redisNodePort));return new ShardedJedisPool(jedisPoolConfig(), jedisShardInfos);}
}

三、Service 层

在src/main/java/com/itdragon/service 目录下创建 UserService.java 文件,它负责三件事情
第一件事情:验证用户信息是否正确,并将登录成功的用户信息保存到Redis数据库中。
第二件事情:负责判断用户令牌是否过期,若没有则刷新令牌存活时间。
第三件事情:负责从Redis数据库中删除用户信息。

package com.itdragon.service;@Service
@Transactional
@PropertySource(value = "classpath:redis.properties")
public class UserService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate JedisClient jedisClient;@Value("${REDIS_USER_SESSION_KEY}")private String REDIS_USER_SESSION_KEY;@Value("${SSO_SESSION_EXPIRE}")private Integer SSO_SESSION_EXPIRE;public Result registerUser(User user) {// 检查用户名是否注册,一般在前端验证的时候处理,因为注册不存在高并发的情况,这里再加一层查询是不影响性能的if (null != userRepository.findByAccount(user.getAccount())) {return Result.build(400, "");}userRepository.save(user);// 注册成功后选择发送邮件激活。现在一般都是短信验证码return Result.build(200, "");}public Result userLogin(String account, String password,HttpServletRequest request, HttpServletResponse response) {// 判断账号密码是否正确User user = userRepository.findByAccount(account);if(user == null){return Result.build(400, "账号名或密码错误");}if (!CheckUtils.decryptPassword(user, password)) {return Result.build(400, "账号名或密码错误");}// 生成tokenString token = UUID.randomUUID().toString();// 清空密码和盐避免泄漏String userPassword = user.getPassword();String userSalt = user.getSalt();user.setPassword(null);user.setSalt(null);// 把用户信息写入 redisjedisClient.set(REDIS_USER_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user));// user 已经是持久化对象了,被保存在了session缓存当中,若user又重新修改了属性值,那么在提交事务时,此时 hibernate对象就会拿当前这个user对象和保存在session缓存中的user对象进行比较,如果两个对象相同,则不会发送update语句,否则,如果两个对象不同,则会发出update语句。user.setPassword(userPassword);user.setSalt(userSalt);// 设置 session 的过期时间jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);// 添加写 cookie 的逻辑,cookie 的有效期是关闭浏览器就失效。CookieUtils.setCookie(request, response, "USER_TOKEN", token);// 返回tokenreturn Result.ok(token);}public void logout(String token) {jedisClient.del(REDIS_USER_SESSION_KEY + ":" + token);}public Result queryUserByToken(String token) {// 根据token从redis中查询用户信息String json = jedisClient.get(REDIS_USER_SESSION_KEY + ":" + token);// 判断是否为空if (StringUtils.isEmpty(json)) {return Result.build(400, "此session已经过期,请重新登录");}// 更新过期时间jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);// 返回用户信息return Result.ok(JsonUtils.jsonToPojo(json, User.class));}
}

四、Controller 层

负责跳转登录页面跳转,负责用户的登录,退出,获取令牌的操作。UserController.java和PageController.java

package com.itdragon.controller;@Controller
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping(value="/login", method=RequestMethod.POST)@ResponseBodypublic Result userLogin(String username, String password,HttpServletRequest request, HttpServletResponse response) {try {Result result = userService.userLogin(username, password, request, response);return result;} catch (Exception e) {e.printStackTrace();return Result.build(500, "");}}@RequestMapping(value="/logout/{token}")public String logout(@PathVariable String token) {userService.logout(token); // 思路是从Redis中删除key,实际情况请和业务逻辑结合return "back";}@RequestMapping("/token/{token}")@ResponseBodypublic Object getUserByToken(@PathVariable String token) {Result result = null;try {result = userService.queryUserByToken(token);} catch (Exception e) {e.printStackTrace();result = Result.build(500, "");}return result;}
}
package com.itdragon.controller;@Controller
public class PageController {@RequestMapping("/login")public String showLogin(String redirect, Model model) {model.addAttribute("redirect", redirect);return "login";}
}

五、视图层

一个简单的登录页面和资源展示页面。login.jsp、index.jsp和indexHomePage.jsp

六、Spring 自定义拦截器

这里是另外一个项目 service-test-sso 中的代码,首先在src/main/resources/spring/springmvc.xml 中配置拦截器,设置哪些请求需要拦截

 <context:component-scan base-package="com.it.controller" /><mvc:annotation-driven /><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/" /><property name="suffix" value=".jsp" /></bean><!-- 资源映射 --><mvc:resources location="/WEB-INF/static/" mapping="/static/**"/><!-- 拦截器配置 --><mvc:interceptors><mvc:interceptor><mvc:mapping path="/indexHomePage/**"/><bean class="com.it.interceptors.UserLoginHandlerInterceptor"/></mvc:interceptor></mvc:interceptors>

UserLoginHandlerInterceptor.java

package com.it.interceptors;public class UserLoginHandlerInterceptor implements HandlerInterceptor {public static final String COOKIE_NAME = "USER_TOKEN";@Autowiredprivate UserService userService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {String token = CookieUtils.getCookieValue(request, COOKIE_NAME);User user = this.userService.getUserByToken(token);if (StringUtils.isEmpty(token) || null == user) {// 跳转到登录页面,把用户请求的url作为参数传递给登录页面。response.sendRedirect("http://localhost:8081/login?redirect=" + request.getRequestURL());// 返回falsereturn false;}// 把用户信息放入Requestrequest.setAttribute("user", user);// 返回值决定handler是否执行。true:执行,false:不执行。return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {}}

七、操作步骤

测试思路:
第一步:注册用户,执行sso 项目下SpringbootStudyApplicationTests.java 单元测试类中的 registerUser() 方法添加用户。
第二步:开启sso服务。
第三步:再开启两个service-test-sso服务。
第四步:在service-test-sso服务页面点击“访问主页”按钮进入权限页面测试。

八、sso项目结构

service-test-sso项目结构

访问主页

点击登录

用户表存储如下

依次通过访问如下链接:
http://localhost:8083/
http://localhost:8081/login?redirect=/indexHomePage
http://localhost:8082/
然后直接就可以不用登录就可以访问资源了,实现SSO功能。

代码链接
代码

转载文章:
https://www.cnblogs.com/itdragon/p/8094722.html

基于SpringBoot实现单点登录系统相关推荐

  1. 单点登录系统实现基于SpringBoot

    今天的干货有点湿,里面夹杂着我的泪水.可能也只有代码才能让我暂时的平静.通过本章内容你将学到单点登录系统和传统登录系统的区别,单点登录系统设计思路,Spring4 Java配置方式整合HttpClie ...

  2. 基于Cookie的单点登录(SSO)系统介绍

    基于Cookie的单点登录(SSO)系统介绍 SSO的概念: 单点登录SSO(Single Sign-On)是身份管理中的一部分.SSO的一种较为通俗的定义是:SSO是指访问同一服务器不同应用中的受保 ...

  3. SpringBoot 简单实现仿CAS单点登录系统

    SpringBoot 简单实现仿CAS单点登录系统 新境界开源开源SSO项目介绍 新境界开源SSO项目实现原理大致如下: 新境界开源SSO项目登录流程介绍 新境界开源SSO项目授权登录流程介绍 新境界 ...

  4. SpringBoot+MyBatis+Redis实现SSO单点登录系统(二)

    SpringBoot+MyBatis+Redis实现SSO单点登录系统(二) 三.代码 配置文件配置数据库,redis等相关的信息. # See http://docs.spring.io/sprin ...

  5. SpringBoot+MyBatis+Redis实现SSO单点登录系统(一)

    SpringBoot+MyBatis+Redis实现SSO单点登录系统(一) 一.SSO系统概述 SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可 ...

  6. 基于云端的通用权限管理系统,SAAS服务,基于SAAS的权限管理,基于SAAS的单点登录SSO,企业单点登录,企业系统监控,企业授权认证中心...

    基于云端的通用权限管理系统 SAAS服务 基于SAAS的权限管理 基于SAAS的单点登录SSO 基于.Net的SSO,单点登录系统,提供SAAS服务 基于Extjs 4.2 的企业信息管理系统 基于E ...

  7. Spring Cloud入门-Oauth2授权之基于JWT完成单点登录(Hoxton版本)

    文章目录 Spring Cloud入门系列汇总 摘要 单点登录简介 创建oauth2-client模块 修改授权服务器配置 网页单点登录演示 调用接口单点登录演示 oauth2-client添加权限校 ...

  8. SSO单点登录系统的实战运用

    通用介绍 单点登录系统,简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.任何SSO框架都需要创建统一的认 ...

  9. codeigniter 禁止ip登录_「开源资讯」baigo SSO v4.0 beta-3 发布,单点登录系统

    来源:https://www.oschina.net/news/117020/baigo-sso-4-beta3-released 简介 baigo SSO 是一款基于 HTTP 协议的单点登录系统, ...

  10. 互联网分布式微服务云平台规划分析--SSO单点登录系统

    介绍 鸿鹄云架构[SSO单点登录系统]为所有微服务提供统一的用户认证服务,系统本身属于微服务模式,使用JWT+Redis分布式存储方案,确保不同微服务.系统之间的安全通讯和统一用户校验.认证.在整个服 ...

最新文章

  1. android 之 Intent、broadcast
  2. CF498C Array and Operations(数论 + 最大流)
  3. Redis 使用 10 个小技巧,请收下!
  4. Linux rsync 命令参数详解
  5. java 权限控制 demo_Java-访问控制权限
  6. mysql 日期详解_在MySQL中解析日期
  7. CSS基础汇总——点击标题跳转详细博客【学习笔记】
  8. python计算两个正整数的和差积商_已知两个正整数的和与积求这两个数
  9. 大数据工程应用 数据分析_大数据工程的方式
  10. IT服务台方案:提供完整的业务流程视图
  11. 简述人工智能的发展历程图_人工智能的发展进程及现状
  12. php仿qq登录界面安卓,Android_Android仿QQ登陆窗口实现原理,今天根据腾讯qq,我们做一个 - phpStudy...
  13. Excel解析easyexcel工具类
  14. vue仿淘宝放大镜插件 vue-piczoom的使用问题
  15. 如何进行架构技术选型
  16. 关于微信分享接口,手机分享时候描述变链接的解决方法以及图片logo不显示问题解决方法
  17. 电脑在线一键安装win10系统教程分享
  18. 张飞实战电子第一部总结笔记
  19. 个人管理|曾航:给年轻人的写作课(下)
  20. 45纳米和堆叠技术在低暗电流和低噪声的0.9微米像素CMOS图像传感器处理的应用

热门文章

  1. <EDEM 基础案例02>Rock Box
  2. android shn1 获取_华为光猫获得Root Shell(shell的root权限)详细说明,接力sdgaojian发帖。...
  3. 软件工程之软件质量管理(SQA)
  4. mac php开发集成环境,MAC OS X下php集成开发环境mamp
  5. window10运行不了1stopt_1stOpt批量处理方程
  6. stm32实用技巧:JLINK接口定义和使用JTAG或SW下载程序
  7. RiceQuant开源项目Rqalpha运行演示策略的错误“ERROR 'figure' is an unknown keyword argument”...
  8. 斐讯K2P B1 博通TTL刷机方法
  9. 魅族Android版本,魅族Flyme安卓版本
  10. Ubuntu系统通用快捷键