由于您的代码是静态的(意味着您有一组可以保护的静态资源),除了增加访问粒度之外,我不理解您对添加安全角色的要求。我可以在模块化环境中看到此要求,其中代码不是静态的,在这种情况下,您需要支持稍后部署声明的其他安全角色。
也就是说,我必须实现类似的东西,一个安全系统,它支持:
- 添加(声明)/删除角色而不重新部署;
- 将用户与这些角色相关联。
我将描述我所做的高层次抽象,并希望它会给你一些有用的想法。
首先,实现一个@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
}
}
为了允许动态配置(即声明角色、关联用户),构建一个使用相同 @EJB 的 UIMySecurityDataManager
来 CRUD 登录模块将用于确定安全角色的安全设置。
现在,您可以按照您想要的方式打包这些,只需确保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 中删除与该部分相关的安全角色
mydomain
,任何人都将无法使用它。
最好的部分,尤其是关于#2 的部分是没有重新部署。此外,无需编辑 XML 来覆盖使用注释声明的默认安全设置(假设您的界面比这更好)。
干杯! 我很乐意提供更多细节,但就目前而言,这至少应该告诉你是否需要它们。