公司以前使用了Jifnal做了一个基础后台管理系统,主要使用了Jifnal+Shiro进行开发.随后交由我
进行后续的升级以及功能的完善.由于jfinal和shiro集成的时候默认使用的Ehcache对集群部署以
及分布式部署会出问题,所以替换为Redis,并对 Session管理重写支持Redis缓存存储.
随着需求不断升级,项目需要支持SSO单点登录.由于Jifnal本身在集成单点框架上资料非常少无法
入手,在咨询官方作者 后,答复是可以自己写一个类似的功能代码量也不大.但是考虑到作为公司
一个基础平台在后续如果需要集成或者对接其 他公司系统时候,无疑是灾难.经过研究以及网上
搜集资料后整理成为一个最强全家桶方案.
https://github.com/apereo/cas
版本:
服务端 5.3.x
客户端 3.5.0
cas服务端搭建不在赘述
具体搭建传送门 : https://www.cnblogs.com/ll409546297/p/10410972.html
pac4j 是一个 java 的安全引擎.
在一套标准的 interface 下,
提供了很多种认证机制: form 表单登录, JWS, cas, OAuth 等等.
提供了很多收授权和权限检查机制: role/permissions, CORS, CSRF, HTTP Security headers
提供了跟很多框架的集成方式: springboot, play, shiro, spring security
这些认证方式都是可插拔的, 你可以同时使用其中的一种或几种.
源码 https://github.com/bujiio/buji-pac4j
官方Demo https://github.com/pac4j/buji-pac4j-demo
版本
io-buji 4.1.1
pac4j-core 3.9.0
友情提示
官方demo中的cas启动访问会报500
主要原因是没有配置callbackFilter
我追加的配置:
callbackFilter=io.buji.pac4j.filter.CallbackFilter
callbackFilter.config = $config
callbackFilter.defaultUrl = http://x.x.x.x:port
callbackFilter.defaultClient = $clients
defaultUrl地址是客户端地址,这个可以不配
最新版本的Pac4j的包使用Java11 编译的如果项目使用的JDK 版本低就很难受再次强调我用的版本
io-buji 4.1.1,pac4j-core 3.9.0
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>JFinal</display-name>
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
<filter-name>shiro</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>shiro</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置JFinal拦截器 -->
<filter>
<filter-name>jfinal</filter-name>
<filter-class>com.jfinal.core.JFinalFilter</filter-class>
<init-param>
<param-name>configClass</param-name>
<param-value>com.ljph.seckill.common.MyConfig</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jfinal</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
shiro.ini
[main]
#pac4j clients
clients = org.pac4j.core.client.Clients
clients.callbackUrl = http://192.168.1.185:8090/callback
clients.clients = $casClient,$headerClient
#pac4j session
sessionStore=com.bz.platform.common.ext.shiro.cas.MyShiroSessionStore
#pac4j config
config = org.pac4j.core.config.Config
config.clients = $clients
config.sessionStore=$sessionStore
#pac4j realm
pac4jRealm = com.bz.platform.common.ext.shiro.realm.MyPac4jRealm
#io.buji.pac4j.realm.Pac4jRealm
#是否开启缓存
pac4jRealm.cachingEnabled=true
pac4jRealm.authenticationCacheName=Pac4jRealm_
#认证缓存开关 在没有解决cas服务端退出可以清除客户端shiro缓存的前提下千万不要开启
#否则在服务端退出后重新登录并访问客户端会取缓存数据进行校验出现Pac4jToken认证异常
pac4jRealm.authenticationCachingEnabled=false
#授权缓存开关
pac4jRealm.authorizationCachingEnabled=true
#pac4j subject
pac4jSubjectFactory = io.buji.pac4j.subject.Pac4jSubjectFactory
securityManager.subjectFactory = $pac4jSubjectFactory
#logout handler
casLogoutHandler=org.pac4j.core.logout.handler.DefaultLogoutHandler
casLogoutHandler.destroySession=true
#cas config
casConfig = org.pac4j.cas.config.CasConfiguration
casConfig.loginUrl = http://192.168.1.110:8080/cas/login
casConfig.prefixUrl = http://192.168.1.110:8080/cas/
casConfig.logoutHandler=$casLogoutHandler
#parameter client
parameterClient = org.pac4j.http.client.direct.ParameterClient
parameterClient.parameterName = X-Token
parameterClient.authenticator = $jwtAuthenticator
parameterClient.supportGetRequest = true
parameterClient.supportPostRequest = true
# X-Token为请求中header的参数key
# authc是token中开头主动添加的字符 因为在请求token校验时候会检验token是否以指定的字符串开头的
headerClient=org.pac4j.http.client.direct.HeaderClient
headerClient.headerName = X-Token
headerClient.prefixHeader = authc
headerClient.authenticator = $jwtAuthenticator
#cas client
casClient=com.bz.platform.common.ext.shiro.cas.MyCasClient
#org.pac4j.cas.client.CasClient
casClient.configuration = $casConfig
#RedisCache
redisCacheManager = com.bz.platform.common.redis.cache.RedisCacheManager
securityManager.cacheManager = $redisCacheManager
#redis管理session
sessionDAO = com.bz.platform.common.redis.session.OnlineSessionDao
sessionManager = com.bz.platform.common.redis.session.MyWebSessionManager
sessionDAO.activeSessionsCacheName = shiro-activeSessionCache
sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager = $sessionManager
#session监听 (主要用于监听session 创建和删除,暂时不用关注)
sessionListener =com.bz.platform.common.redis.session.ShiroSessionListener
securityManager.sessionManager.sessionListeners =$sessionListener
sessionManager.sessionValidationSchedulerEnabled = false
sessionManager.sessionValidationInterval=5000
# 超时时间60*60*1000 3600000 如果不设置默认30min
securityManager.sessionManager.globalSessionTimeout = 1800000
#在线用户管理 客户端数量限制不建议加在此处
kickout=com.bz.platform.common.ext.filter.KickoutSessionControlFilter
kickout.cacheManager=$redisCacheManager
kickout.sessionManager=$sessionManager
kickout.kickoutUrl=/index
#一个终端类型只允许登录一次
kickout.maxSession=1
signingConfig = org.pac4j.jwt.config.signature.SecretSignatureConfiguration
signingConfig.secret = 12345678901234567890123456789012
encryptionConfig = org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration
encryptionConfig.secret = 12345678901234567890123456789012
jwtAuthenticator = org.pac4j.jwt.credentials.authenticator.JwtAuthenticator
jwtAuthenticator.signatureConfiguration = $signingConfig
jwtAuthenticator.encryptionConfiguration = $encryptionConfig
#authFilter
casFilter = io.buji.pac4j.filter.SecurityFilter
casFilter.config = $config
casFilter.clients = MyCasClient
#callback
callbackFilter=io.buji.pac4j.filter.CallbackFilter
callbackFilter.config = $config
callbackFilter.defaultUrl = http://192.168.1.185:8090/
#callbackFilter.defaultClient = $clients
#pac4j logout Filter
pac4jLogout = io.buji.pac4j.filter.LogoutFilter
pac4jLogout.config = $config
pac4jLogout.localLogout = true
pac4jLogout.centralLogout = true
pac4jLogout.defaultUrl=http://192.168.1.185:8090/callback?client_name=CasClient
pac4jLogout.logoutUrlPattern = http://192.168.1.185:8090/.*
#jwt filter
jwtSecurityFilter = io.buji.pac4j.filter.SecurityFilter
jwtSecurityFilter.config = $config
jwtSecurityFilter.clients = HeaderClient
[urls]
#首次进入/访问项目时进行cas认证过滤
/ = casFilter
#cas认证通过后系统回调
/callback = callbackFilter
#登出系统过滤
/pac4jLogout = pac4jLogout
#jwt过滤校验(所有请求都进行校验没有携带token或者token错误)
/** = jwtSecurityFilter
#kickout先暂停 等待cas服务端修改完毕在考虑是否继续存在
/** = kickout
重写Seesion以及sessionManager主要目的是为了缓存在Redis中这块可以不要不影响系统正常使用
引用文本
POM.xml
再次强调我只列了核心包,如果缺包的话jfinal就不说了 按官方的来,Shiro和cas已经随Pac4j引入其他的包可以参考buji-pac4j-demo官方demo 不过demo的jar下载有时候会比较慢
没有贴全pom是因为项目分了好几个moudles 所以没有全部贴
<properties>
<pac4jVersion>3.9.0</pac4jVersion>
<bujiVersion>4.1.1</bujiVersion>
</properties>
<!-- pac4j-core -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-core</artifactId>
</dependency>
<!--cas pac4j 相关jar-->
<dependency>
<groupId>io.buji</groupId>
<artifactId>buji-pac4j</artifactId>
<version>${bujiVersion}</version>
<exclusions>
<exclusion>
<artifactId>org.pac4j</artifactId>
<groupId>pac4j-core</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-core</artifactId>
<version>${pac4jVersion}</version>
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-cas</artifactId>
<version>${pac4jVersion}</version>
<exclusions>
<exclusion>
<artifactId>org.pac4j</artifactId>
<groupId>pac4j-core</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-jwt</artifactId>
<version>${pac4jVersion}</version>
<exclusions>
<exclusion>
<artifactId>org.pac4j</artifactId>
<groupId>pac4j-core</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-http</artifactId>
<version>${pac4jVersion}</version>
<exclusions>
<exclusion>
<artifactId>org.pac4j</artifactId>
<groupId>pac4j-core</groupId>
</exclusion>
</exclusions>
</dependency>
基础的配置引入已经基本完毕了.接下来就是JWTtoken的生成
JWT Token
public void index() {
try {
// 获取用户身份
Pac4jPrincipal p = SecurityUtils.getSubject().getPrincipals().oneByType(Pac4jPrincipal.class);
CommonProfile profile = p.getProfile();
// 这是取出cas服务端返回的用户数据
Map profileMap=profile.getAttributes();
// 生成token
final JwtGenerator generator = new JwtGenerator(new SecretSignatureConfiguration("12345678901234567890123456789012"));
String token = null;
final PrincipalCollection col = SecurityUtils.getSubject().getPrincipals();
if (col != null) {
final Pac4jPrincipal principal = col.oneByType(Pac4jPrincipal.class);
if (principal != null) {
token = generator.generate(principal.getProfile());
}
}
// 因为我们是前后端分离前端用的vue所以token传到前端是个问题
// 开发阶段可以使用下面的方式先把token传过去,正式部署阶段因为前后端打包放一起可以使用Cookie存储
// 将签发的 JWT token 设置到 HttpServletResponse 的 Header中,并重写向vue前端页面
getResponse().setHeader("token", token);
//getResponse().sendRedirect("http://192.168.0.41:8088/#/stats/casindex");
getResponse().sendRedirect("http://192.168.200.185:9527/#/"+"?X-Token=authc"+token);
} catch (Exception e) {
e.getStackTrace();
}
renderNull();
}
到此基本已经集成完毕并且可以使用了,后面我抽时间继续补充完善.后续如果大家需求大的话我考虑吧上述的一些配置或者类整理传到git上,先这样吧 有问题可以先留言.