Security - 輕量級Java身份認證、訪問控制安全框架


前言

  此框架由小菜獨立開發,並且已經在生產環境中運行大約一年時間。

  也就是說,Security 框架寫出來有一段時間了,但是一直沒有公布、開源,經過不斷迭代完善,終於算是拿得出手啦~

  Security 框架存在的意義並不是為了替代 Shiro 或 Spring Security ,而且提供另一種選擇。

  當讀者因為現有安全框架的復雜繁瑣而苦惱時,為什么不嘗試一下 Security 呢?

  請原諒小菜在本文直接照搬 GitHub 的 README,以后小菜會陸續完善使用教程和相關 Demo ,敬請關注~

  最后希望讀者能給出寶貴意見、及時反饋問題,來幫助小菜繼續完善框架。

README

簡介

  本框架基於Spring MVC開發,是一款輕量級的安全認證框架。

  拋棄ShiroSpring Security等安全框架繁瑣的配置,改為注解實現權限管理,配合Spring MVCRequestMapping注解,完美實現細粒度的權限控制。

  本框架以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,實現newCodesendToPhonesendToEmail方法,這三個方法是公共方法,但必須交給使用者實現,因為不同的項目發送短信、郵件的方式不盡相同,生成驗證碼的規則也不盡相同。然后在項目中以AbstractPhoneEmailSecurityCaptchaService為基礎,派生出具體的業務類,比如發送注冊驗證碼的業務類RegisterCaptchaService,繼承AbstractPhoneEmailSecurityCaptchaService,然后實現nametitlecontent方法即可。
  • 如果需要使用圖形驗證碼,則定義一個抽象類,假設名稱為AbstractSecurityImageCaptchaService,繼承模塊中的AbstractSecurityImageCaptcha,實現newCode方法,生成的驗證碼取決於實際項目中圖形生成器的能力,避免生成無法被圖形生成器識別的字符,當然,圖形生成器您自己實現,看着辦。然后在項目中以AbstractSecurityImageCaptchaService為基礎,派生出具體的業務類,比如登陸圖形驗證碼的業務類LoginImageCaptchaService,繼承AbstractSecurityImageCaptchaService,然后實現name方法即可。

  通過以上描述可以看出,驗證碼模塊只是封裝了繁瑣的驗證碼發送標記、驗證等操作,並不干預具體的發送實現。

  使用者封裝好適合自己的抽象基類后,不論任何業務,只需要繼承抽象基類即可輕松實現,並天然實現業務之間的隔離。

  總結一下,使用者只需要關注發送手機短信發送郵件生成驗證碼文本生成驗證碼圖片具體實現,然后根據具體業務設定好驗證碼標題驗證碼內容業務名稱(用來隔離業務)即可。

使用

  經過前期配置之后,使用就非常簡單了!

  只需要在Controller層使用Security注解即可,Security注解具體使用方法請參考源碼注釋。

  本框架只關注角色認證,而不關注角色的存儲、定義,徹底實現安全認證框架與實際項目之間的解耦。

  在定義角色名稱時,不應該出現框架已經占用的關鍵字,包括:[]{}><,:,否則會引起沖突。

GitHub 項目地址

Security

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM