因爲你的代碼是靜態的(意思是你有一個靜態組可確保資源的),我不明白你從增加訪問粒度增加安全角色休耕的要求。我可以在代碼不是靜態的模塊化環境中看到此需求,在這種情況下,您需要支持稍後部署聲明的其他安全角色。
這麼說,我不得不實施類似的東西,支持安全系統:
- 增加(聲明)/重新部署不刪除角色;
- 將用戶關聯到這些角色。
我會在高層次的抽象中描述我做了什麼,希望它會給你一些有用的想法。
首先,實施@EJB
,這樣的事情:
@Singleton
@LocalBean
public class MySecurityDataManager {
public void declareRole(String roleName) {
...
}
public void removeRole(String roleName) {
...
}
/**
* Here, I do not know what your incoming user data looks like such as:
* do they have groups, attributes? In my case I could determine user's groups
* and then assign them to roles based on those. You may have some sort of
* other attribute or just plain username to security role association.
*/
public void associate(Object userAttribute, String roleName) {
...
}
public void disassociate(Object userAttribute, String roleName) {
...
}
/**
* Here basically you inspect whatever persistence method you chose and examine
* your existing associations to build a set of assigned security roles for a
* user based on the given attribute(s).
*/
public Set<String> determineSecurityRoles(Object userAttribute) {
...
}
}
然後你實現自定義javax.security.auth.spi.LoginModule
。我建議從頭開始實現它,除非你知道容器提供的抽象實現對你有用,它不適合我。另外,我建議你熟悉下,如果你不是,以便更好地瞭解我前往:
public class MyLoginModule implements LoginModule {
private MySecurityDataManager srm;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
// make sure to save subject, callbackHandler, etc.
try {
InitialContext ctx = new InitialContext();
this.srm = (MySecurityDataManager) ctx.lookup("java:global/${your specific module names go here}/MySecurityDataManager");
} catch (NamingException e) {
// error logic
}
}
@Override
public boolean login() throws LoginException {
// authenticate your user, see links above
}
@Override
public boolean commit() throws LoginException {
// here is where user roles get assigned to the subject
Object userAttribute = yourLogicMethod();
Set<String> roles = srm.determineSecurityRoles(userAttribute);
// implement this, it's easy, just make sure to include proper equals() and hashCode(), or just use the Jboss provided implementation.
Group rolesGroup = new SimpleGroup("Roles", roles);
// assuming you saved the subject
this.subject.getPrincipals().add(rolesGroup);
}
@Override
public boolean abort() throws LoginException {
// see links above
}
@Override
public boolean logout() throws LoginException {
// see links above
}
}
爲了允許動態配置(即聲明角色,關聯用戶),構建一個UI臨時表t使用相同的@EJB MySecurityDataManager
來刪除登錄模塊用來確定安全角色的安全設置。
現在,您可以按照自己想要的方式進行打包,只需確保MyLoginModule
可以查找MySecurityDataManager
並將其部署到容器中。我曾經在JBoss上工作,並且提到了JBoss,所以這也適用於你。更健壯的實現將包括LoginModule配置中的查找字符串,然後您可以在運行時從initialize()
方法的選項映射中讀取。下面是JBoss的一個示例配置:
<security-domain name="mydomain" cache-type="default">
<authentication>
<login-module flag="required"
code="my.package.MyLoginModule"
module="deployment.${your deployment specific info goes here}">
<module-option name="my.package.MySecurityDataManager"
value="java:global/${your deployment specific info goes here}/MySecurityDataManager"/>
</login-module>
</authentication>
</security-domain>
在這一點上,你可以使用這個安全域mydomain
管理容器中的任何其他部署的安全性。
這裏有幾個使用場景:
- 部署新的.war,並將其分配給
mydomain
安全域。 .war在其代碼中帶有預定義的安全註釋。您的安全領域最初並沒有他們,所以沒有用戶可以登錄。但是在部署之後,由於安全角色有充分的文檔記錄,您可以打開您編寫的配置界面並聲明這些角色,然後爲其分配用戶。現在,他們可以登錄。
- 經過幾個月的部署,您不再希望用戶能夠訪問特定的戰爭參數。從您的
mydomain
中刪除與.war相關部分的安全角色,任何人都無法使用它。
最好的部分,特別是關於#2的是不重新部署。另外,不要編輯XML以覆蓋使用註釋聲明的默認安全設置(假設您的界面比這更好)。
乾杯! 我很樂意提供更多細節,但現在,這應該至少告訴你是否需要它們。
感謝您的回答: 1.我不想在代碼中擁有'@ DeclareRoles'的原因是因爲它是靜態的,角色可以隨時間變化(維護)。 2.1我不想在REST代碼中擁有用戶名。想想更深層次的背景,我沒有名字,我不能總是提供他。我可以很容易地從reqeust標題中讀取參數,但那不是我的目標。 2.2我喜歡用'SesseionContext'和用戶角色來實現JEE安全,但爲什麼它必須是靜態的,而不僅僅是程序化的? –
只是爲了「爲什麼它必須是靜態的而不僅僅是程序化的?」角色是靜態的,因爲一般來說,應用程序不應該在沒有重新部署的情況下動態增加其安全功能。儘管您可以決定是否在web.xml或bean中指定角色以簡化代碼維護,但最好不要避免代碼重新部署。 – Xiangyu
這是一個很好的觀點,並在web.xml中改變它並不是什麼大不了的事情(不需要重新部署)。我一定會考慮它。從我的項目來看,如果某些事情發生變化,重要的是不要重新部署,因爲它是客戶的一種自定義代碼,不希望觸及代碼,也不會要求供應商始終這樣做。這就是爲什麼我正在尋找一個簡單的可配置解決方案。但除此之外,我認爲有一種方法可以實現我的方法,並且我會保持獎勵以獲得儘可能多的迴應:D –