JFinal最强集成方案

Jifnal+Shiro+Pac4j+Cas+Redis

简介

         公司以前使用了Jifnal做了一个基础后台管理系统,主要使用了Jifnal+Shiro进行开发.随后交由我
    进行后续的升级以及功能的完善.由于jfinal和shiro集成的时候默认使用的Ehcache对集群部署以
    及分布式部署会出问题,所以替换为Redis,并对 Session管理重写支持Redis缓存存储.

         随着需求不断升级,项目需要支持SSO单点登录.由于Jifnal本身在集成单点框架上资料非常少无法
 入手,在咨询官方作者 后,答复是可以自己写一个类似的功能代码量也不大.但是考虑到作为公司
 一个基础平台在后续如果需要集成或者对接其 他公司系统时候,无疑是灾难.经过研究以及网上

搜集资料后整理成为一个最强全家桶方案.

CAS

https://github.com/apereo/cas

版本:
服务端 5.3.x
客户端 3.5.0

cas服务端搭建不在赘述

具体搭建传送门 : https://www.cnblogs.com/ll409546297/p/10410972.html

Pac4J

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

1
<?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

1
[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 所以没有全部贴

1
<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

1
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上,先这样吧 有问题可以先留言.