公司有一个复杂项目,需要建设多个应用系统。例如:用户管理系统、门户系统、客户管理系统、采购系统、销售管理系统、库存系统等。所有系统的用户均需要通过用户管理系统进行统一授权,每个角色通过门户系统登录一次就可以跳转到所有授权的子系统。这种场景我们就需要建设一套单点登录的模式。常见的有CAS、OAuth2、LDAP等。初步调研后发现每种类型都有很多坑,而且最主要的是开发工作量大。有没有一种简单的方式实现呢?有的,那就是SpringSession。本文我先介绍如何搭建,再介绍选型原理。
快速入门前,至少需要掌握IDEA开发工具的使用、Redis的启动、SpringBoot、SpringMVC、SpringSecurity的开发运行原理。实在查询不到的朋友可以留言,后续为大家补充。
快速入门的目标是启动两个独立的WEB应用,访问A应用登录后,访问B应用也可以自动登录,并且二者sessionID是一致的。
步骤如下:
1、启动Redis 2、构建demo工程 3、修改配置 4、运行测试
下载redis地址为:https://github.com/MSOpenTech/redis/releases
推荐下载win64位版本的
启动redis,该版本是绿色版,需要手动启动。可以将压缩包解压至D:\Program Files\Redis,如图:
使用如下命令启动:
d:
cd D:\Program Files\Redis
redis-server.exe redis.windows.conf
启动成功后,如图所示:
开启IDEA,默认认为你的IDEA已经配置好MAVEN。选择Spring Initializr,需要JDK1.8环境。无需修改配置点击下一步即可。
配置工程的一些设置,可以根据自己需要修改,为方便测试Packaging需要为Jar,否则你需要单独建立两个独立容器如Tomcat或jetty。修改完毕后点击下一步。
选择SpringSecurity、SpringSession、SpringWeb、SpringDataRedis、Springboot DevTools,如下图:
点击finish
产生如下图所示的model即创建成功。
删除application.properties文件,application.yml、application-dev.yml、application-pro.yml。关于他们之间的关系可以想见我的另外一篇文章。需要特别注意的是两个配置文件中port需要不一样,因为后续运行中需要启动两个独立的应用。
https://mp.csdn.net/console/editor/html/104457279
application.yml文件配置如下:
spring:
profiles:
#激活开发环境
active: dev
application-dev.yml文件配置如下:
server:
port: 8080
spring:
session:
redis:
flush-mode: on_save
namespace: session.example
cleanup-cron: 0 * * * * *
store-type: redis
timeout: 1800
redis:
host: localhost
port: 6379
jedis:
pool:
max-active: 100
max-wait: 10
max-idle: 10
min-idle: 10
database: 0
application-pro.yml文件配置如下:
server:
port: 8081
spring:
session:
redis:
flush-mode: on_save
namespace: session.example
cleanup-cron: 0 * * * * *
store-type: redis
timeout: 1800
redis:
host: localhost
port: 6379
jedis:
pool:
max-active: 100
max-wait: 10
max-idle: 10
min-idle: 10
database: 0
开发一个IndexController用于登录成功后的提示以及我们观察sessionID。代码如下:
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 登录成功后的首页控制器
*
* @author bowang
*/
@RestController
public class IndexController {
/**
* 首页
*
* @param request request对象
* @return 当前sessionID
* @author bowang
*/
@GetMapping("/")
public String index( HttpServletRequest request) {
HttpSession session = request.getSession();
String returnStr = "create session, sessionId is:" + session.getId();
System.out.println(returnStr);
return returnStr;
}
}
配置完成后运行DemoApplication文件,或者采用如下方式启动。不提示错误则表示运行成功。
使用maven的package将项目打包,如下图:
打包成功后如下图,将jar文件拷贝至d:,并更名为demo.jar。
分别启动两个CMD程序
CMD1窗体执行命令
d:
java -jar demo.jar
出现如下提示则启动成功,注意将密码拷贝出来:
CMD2窗体执行命令
d:
java -jar demo.jar --spring.profiles.active=pro
出现如下提示则启动成功,注意将密码拷贝出来:
启动测试,注意springscurity的默认登录用户是user。
访问CMD1的地址,http://localhost:8080,输入用户密码,点击登录。
登录成功后,就会进入如下界面。6a8f353a-3fba-4df1-980e-abdf2d1c93df就是本次登录的sessionID。
访问CMD2的地址,http://localhost:8081,无需输入用户密码,系统自动登录。
快速入门内容完毕。
快速入门写了很多东西,其实核心的工作量就是配置SpringSession和编写一个indexcontroller。在选择这个架构前我也研究了一下CAS和 OAuth2进行选型。我查询的一些资料和我个人的理解如下:
OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息。参见https://www.jianshu.com/p/4f5fcddb4106
很多blog对技术方面说的很细,我仅仅说说我的理解。OAuth典型的应用就是微信、QQ、微博认证。很多手机APP、PC版网站都可以通过微信、QQ、微博快速认证,让你可以优先查看一些资源,后续还需进一步输入你的手机号、姓名、邮箱甚至于身份证等进行进一步认证。在这个架构里面需要有资源使用方、资源提供方、认证服务方三个程序,在我们开篇提到公司内部应用场景时,显得过于复杂。同时这种模式不包含用户注册和授权,在开发时还需要建设一个支持认证服务方,用户注册、授权系统,整体看工作量偏重。最后公司内部应用场景中,资源使用方和资源提供方一般都是同一个应用,显然这种模式对这类场景的认证显得复杂。
CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。详细内容可以参见https://blog.csdn.net/feng27156/article/details/38060099
简单来说就是下载一个CAS的服务端,改造应用系统的认证部分,同时还要在设计过程中考虑如何在浏览器端维护票根。基本逻辑见下图:
站在开发工作量上评估,CAS服务端需要重写,或者至少需要修改服务端的UI,你需要深入理解源码。又或者你按照其逻辑实现/login、/logout、/validate这几个接口,并重写登录认证的客户端。就我个人的经验,不考虑用户注册、认证等工作的实现,一个10年以上工作经验的主程序员,996搞2个星期能做出来。而且开发模式上与SpringBoot+SpringSecurity的开发模式偏差比较大,有很多内容需要重新定制。最坑爹的是部署后,应用系统A和CAS服务端需要频繁的验证票根,在很多云平台的环境中,经常被认为是你的服务器受到攻击,每10次请求可能有1次被拦截。搞了很长时间,才调试好。
经历过上一次的噩梦,我就一直在想能不能有一种方式直接使用SpringSecurity,应用系统间可以共享认证的session。但是每个独立的容器都会为会话创建一个独立的session,如何解决这个问题呢?这个问题一直困扰着我,直到我发现SpringSession。
Spring Session提供了用于管理用户会话信息的API和实现,同时还使得支持群集会话变得微不足道,而不依赖于特定于应用程序容器的解决方案。https://www.springcloud.cc/spring-session.html#introduction
简单说就是就是SpringSession通过重构J2EE的API实现跨应用、跨容器的session互认,既能解决单点登录问题又能解决分布式部署问题。其架构简单到令人发指,配置过程简单到让人身心愉悦。本入门的架构图:
网上很多技术流大神,但是我却没有找SpringBoot+SpringSecurity+SpringSession的入门教程,在官网上看了一下也觉得一般。就准备自己随便试试结果一下就成,其难易程度过于简单可能很多大神就不屑去写这个入门。同时将我自己的一些想法也写出来,欢迎各路大神进来批判。感谢这次疫情,让我能静下心来思考这些问题。最后也祝愿武汉的朋友们加油,再困难的日子终究会去过的,奥利给!!!