Security - 輕量級Java身份認證、訪問控制安全框架
前言
此框架由小菜獨立開發,并且已經在生產環境中運行大約一年時間。
也就是說,Security 框架寫出來有一段時間了,但是一直沒有公布、開源,經過不斷迭代完善,終于算是拿得出手啦~
Security 框架存在的意義并不是為了替代 Shiro 或 Spring Security ,而且提供另一種選擇。
當讀者因為現有安全框架的復雜繁瑣而苦惱時,為什么不嘗試一下 Security 呢?
請原諒小菜在本文直接照搬 GitHub 的 README,以后小菜會陸續完善使用教程和相關 Demo ,敬請關注~
最后希望讀者能給出寶貴意見、及時反饋問題,來幫助小菜繼續完善框架。
README
簡介
本框架基于Spring MVC開發,是一款輕量級的安全認證框架。
拋棄Shiro、Spring Security等安全框架繁瑣的配置,改為注解實現權限管理,配合Spring MVC的RequestMapping注解,完美實現細粒度的權限控制。
本框架以Redis作為持久化數據庫,Ehcache作為內存級緩存,滿足高性能需求。
本框架刪繁就簡,以角色作為權限認證的唯一標準,并非傳統的RBAC權限模型,在這里沒有權限的概念,只有角色,角色就是權限,權限就是角色,因此本框架適合應用于互聯網項目,尤其適合前后端分離模式下的后端接口。
特性
- 高性能(設計簡潔、內置緩存)
- 基于注解
- 安全的密碼加密機制
- 靈活的配置項
- 易于集成、擴展
- Session共享
- 分布式部署
- 實現匿名認證、基礎的登陸認證、基于角色的權限管理、基于范圍表達式的權限管理、HTTP Basic Authentication
- 并發登錄控制
- 基礎的在線會話管理
- 驗證碼框架封裝
- 第三方登錄集成
主要依賴
- Spring MVC,基礎依賴
- Httpclient,第三方登陸依賴
- FastJson,序列化依賴
- Ehcache,緩存依賴
- Redis,持久化依賴
集成
添加Maven項目依賴
<!-- security frame work --> <dependency> <groupId>org.yangyuan</groupId> <artifactId>security</artifactId> <version>0.0.1</version> </dependency>
與Spring MVC集成
<!-- 掃描spring注解 --> <context:component-scan base-package="com.yourself, org.yangyuan.security" /> <!-- 身份認證攔截器 --> <mvc:interceptors> <bean class="org.yangyuan.security.servlet.SecurityInterceptor"></bean> </mvc:interceptors>
添加配置文件
將本項目中的security.properties文件拷貝到真實項目resources根目錄下,與log4j.properties位置相同,即保證編譯后這個文件在classes目錄下。
security.properties說明
#Session有效期
#這是一個相對值,相對于用戶最后一次訪問的時間
#也就是說,只有當用戶超過此時間不活躍,Session才會失效
#單位秒(s)
session.expiresMilliseconds=2592000000
#是否啟用Session垃圾回收器
session.gc.open=true
#Session垃圾回收器Lua腳本
session.gc.script=for i=48,83,1 do local partition if(i > 57) then partition = string.char(i + 39) else partition = string.char(i) end local setkey = 'security:session:set:'..partition local principals = redis.call('ZRANGEBYSCORE', setkey, '-inf', ARGV[1]) redis.call('ZREMRANGEBYSCORE', setkey, '-inf', ARGV[1]) if(principals and (table.maxn(principals) > 0)) then for ii,vv in ipairs(principals) do local hashkey = 'security:session:hash:'..partition redis.call('HDEL', hashkey, vv) end end end
#Session垃圾回收器執行時間間隔
#單位秒(s)
session.gc.gcDelaySecond=86400
#cookie名稱
cookie.name=sid
#cookie域名
cookie.domain=.cospace.xyz
#cookie路徑
cookie.path=/
#此配置為true時,cookie無法通過js腳本操作
cookie.http_only=true
#是否啟用HTTPS
cookie.secure=true
#cookie有效期,一般不需要改動,目前設置的是最大值,相當于永不過期
#因為cookie的生命周期由服務器端維護,所以客戶端不需要關心過期時間
cookie.max_age=315360000
#Redis客戶端連接工廠
#負責提供Redis客戶端連接
common.redisResourceFactory=cc.cospace.web.security.dao.DefaultRedisResourceFactory
#安全管理器實現
core.securityManager=org.yangyuan.security.core.DefaultSecurityManager
#安全唯一標識生成器實現
core.principalFactory=org.yangyuan.security.core.DefaultPrincipalFactory
#緩存管理器實現
core.cacheManager=org.yangyuan.security.core.DefaultCacheManager
#是否復用客戶端subject
#如果設為true,客戶端登陸時如果攜帶有subject信息,那么復用此subject,不再創建新的subject
#如果設為false,則登錄時忽略客戶端攜帶的subject信息,總是創建新的subject
core.useClientSubjectLogin=false
#并發主題控制器
#[org.yangyuan.security.core.MultiportConcurrentSubjectControl]允許同一個賬號同時在不同客戶端登陸
#[org.yangyuan.security.core.SingleConcurrentSubjectControl]同一個賬號同一時刻只能在一個客戶端登陸,如果之前在其他客戶端登陸過,那么之前的登陸將失效
#[org.yangyuan.security.core.RefuseConcurrentSubjectControl]同一個賬號同一時刻只能在一個客戶端登陸,如果之前在其他客戶端登陸過,那么本次登陸將會失敗,除非其他客戶端主動退出登陸
core.concurrentSubjectControl=org.yangyuan.security.core.MultiportConcurrentSubjectControl
#認證回調
#此處理器用來響應認證結果(成功、失敗、拒絕訪問)
#具體的響應依賴于具體的業務,框架只負責通知認證結果
core.securityAuthHandler=cc.cospace.web.security.core.DefaultSecurityAuthHandler
#ehcache緩存數據訪問層(緩存層)
dao.ehcacheSessionDao=org.yangyuan.security.dao.EhcacheSessionDao
#redis數據訪問層(持久化層)
dao.redisSessionDao=org.yangyuan.security.dao.RedisSessionDao
#持久化數據源(用戶名密碼模式)
dao.jdbcRealm=org.yangyuan.security.realm.jdbc.JdbcRealm
#第三方數據源
dao.remoteRealm=org.yangyuan.security.realm.remote.RemoteRealm
#本地認證數據訪問層(用戶名密碼模式)
dao.jdbcSessionDao=org.yangyuan.security.dao.JdbcSessionDao
#第三方登錄認證數據訪問層
dao.remoteSessionDao=org.yangyuan.security.dao.RemoteSessionDao
#用戶名密碼模式登錄適配器
#此適配器實現安全認證與具體項目用戶數據存儲之間的解耦
dao.jdbcRealmAdaptor=userService
#第三方登錄適配器
#此適配器實現安全認證與具體項目用戶數據存儲之間的解耦
dao.remoteRealmAdaptor=userService
#cache在內存中最多可以存放的元素的數量。
#0表示沒有限制。
#如果放入cache中的元素超過這個數值,有兩種可能:
#1、若overflowToDisk的屬性值為true,會將cache中多出的元素放入磁盤文件中。
#2、若overflowToDisk的屬性值為false,會根據memoryStoreEvictionPolicy的策略替換cache中原有的元素。
cache.maxElementsInMemory=10000
#緩存是否永駐內存。
#如果值是true,cache中的元素將一直保存在內存中,不會因為時間超時而丟失。
#因此在這個值為true的時候,timeToIdleSeconds和timeToLiveSeconds兩個屬性的值就不起作用了。
cache.eternal=false
#內存中的元素數量溢出是否寫入磁盤。
#系統會根據標簽<diskStore path="java.io.tmpdir"/>中path的值查找對應的屬性值。
#如果系統的java.io.tmpdir的值是/temp,寫入磁盤的文件就會放在這個文件夾下,文件的名稱是cache的名稱,后綴名為data。
cache.overflowToDisk=false
#是否持久化內存中的緩存到磁盤。
#當這個屬性的值為true時,系統在初始化的時候會在磁盤中查找文件名為cache名稱,后綴名為index的的文件,如CACHE_FUNC.index。
#這個文件中存放了已經持久化在磁盤中的cache的index,找到后把cache加載到內存。
cache.diskPersistent=false
#訪問cache中元素的最大間隔時間。
#如果超過此時間cache中的某個元素沒有任何訪問,那么這個元素將被從cache中清除。
cache.timeToIdleSeconds=900
#cache中元素的總生存時間,cache中的某個元素從創建到消亡的時間。
#從創建開始計時,當超過這個時間,這個元素將被從cache中清除,即便是這個元素被頻繁訪問。
cache.timeToLiveSeconds=7200
#內存存儲與釋放清理策略
#LRU最近最少使用
#LFU歷史訪問頻率最低
#FIFO先進先出
cache.memoryStoreEvictionPolicy=LRU
#普通驗證碼有效期
#單位s
captcha.normal.expireSecond=900
#普通驗證碼多次發送最短時間間隔
#單位s
captcha.normal.minIntervalSecond=50
#圖形驗證碼有效期
#單位s
captcha.image.expireSecond=600
#圖形驗證碼錯誤統計周期
#單位s
captcha.image.wrongPeriodSecond=60
#圖形驗證碼統計周期內允許最大錯誤次數
captcha.image.periodMaxWrongCount=3
具體業務類實現
- common.redisResourceFactory,為框架提供Redis連接,實現
RedisResourceFactory接口。 - core.securityAuthHandler,自定義認證結果行為,用來處理認證成功、未登錄、權限不足的具體業務,實現
SecurityAuthHandler接口。 - dao.jdbcRealmAdaptor,提供用戶名/密碼登陸模式必要的數據,具體出參入參參考源碼注釋,實現
JdbcRealmAdaptor接口。 - dao.remoteRealmAdaptor,提供第三方登陸模式必要的數據,具體出參入參參考源碼注釋,實現
RemoteRealmAdaptor接口。
security.properties文件中配置的所有類型,可以配置成完整類名(包名+類名),也可以配置成spring IOC中的Bean名稱,根據業務情況自由選擇。
一般來講,除非需要自己擴展框架,否則只需要實現具體業務類,然后修改一下cookie相關配置即可,其他配置項均可使用默認配置。
驗證碼模塊使用介紹
驗證碼模塊只實現了公共邏輯,并沒有實現具體的發送邏輯,目的是留給使用者更多的操作空間,使得框架具有更強的適應性。
如果需要使用驗證碼模塊,最佳實踐如下:
- 如果需要使用手機短信、郵箱郵件驗證碼,則定義一個抽象類,假設名稱為
AbstractPhoneEmailSecurityCaptchaService,繼承模塊中的AbstractPhoneEmailSecurityCaptcha,實現newCode、sendToPhone、sendToEmail方法,這三個方法是公共方法,但必須交給使用者實現,因為不同的項目發送短信、郵件的方式不盡相同,生成驗證碼的規則也不盡相同。然后在項目中以AbstractPhoneEmailSecurityCaptchaService為基礎,派生出具體的業務類,比如發送注冊驗證碼的業務類RegisterCaptchaService,繼承AbstractPhoneEmailSecurityCaptchaService,然后實現name、title、content方法即可。 - 如果需要使用圖形驗證碼,則定義一個抽象類,假設名稱為
AbstractSecurityImageCaptchaService,繼承模塊中的AbstractSecurityImageCaptcha,實現newCode方法,生成的驗證碼取決于實際項目中圖形生成器的能力,避免生成無法被圖形生成器識別的字符,當然,圖形生成器您自己實現,看著辦。然后在項目中以AbstractSecurityImageCaptchaService為基礎,派生出具體的業務類,比如登陸圖形驗證碼的業務類LoginImageCaptchaService,繼承AbstractSecurityImageCaptchaService,然后實現name方法即可。
通過以上描述可以看出,驗證碼模塊只是封裝了繁瑣的驗證碼發送標記、驗證等操作,并不干預具體的發送實現。
使用者封裝好適合自己的抽象基類后,不論任何業務,只需要繼承抽象基類即可輕松實現,并天然實現業務之間的隔離。
總結一下,使用者只需要關注發送手機短信、發送郵件、生成驗證碼文本、生成驗證碼圖片具體實現,然后根據具體業務設定好驗證碼標題、驗證碼內容、業務名稱(用來隔離業務)即可。
使用
經過前期配置之后,使用就非常簡單了!
只需要在Controller層使用Security注解即可,Security注解具體使用方法請參考源碼注釋。
本框架只關注角色認證,而不關注角色的存儲、定義,徹底實現安全認證框架與實際項目之間的解耦。
在定義角色名稱時,不應該出現框架已經占用的關鍵字,包括:[、]、{、}、>、<、,、:,否則會引起沖突。
GitHub 項目地址

浙公網安備 33010602011771號