用过Spring Security的朋友一定不会陌生,有个@Secured注解,可以将其加在Service层的方法上,保护某个方法的安全,确保只有授权的角色可以调用该方法。
但是,如果要对Struts2的Action方法进行保护呢?看似加注解是个不合理的需求。但是,有些情况下,例如,一个命名空间下用星号匹配有多个角色,而用精确地址匹配又导致数据太多,数据库不太好维护。在这种情况下,如果一个Action地址,确定以后不会太更换访问角色的话,可以考虑用注解来保护。
可以将命名空间下的全部地址匹配为星号。然后用注解保护该命名空间下的个别Action地址。
首先是一个注解类。
- @Target(METHOD)
- @Retention(RUNTIME)
- public @interface ActionRoles {
- SysRole[] value();
- }
@Target(METHOD)
@Retention(RUNTIME)
public @interface ActionRoles {
SysRole[] value();
}
注解里面是一个枚举类,代表了系统的所有角色枚举。
然后是Struts2的拦截器,在该拦截器内,通过反射,得到加在Action方法注解上的用户角色数组。其中,还通过SpringSecurityUtils取得当前登录的用户,并拿到登录用户的角色,通过和actionRoles循环比较,得到用户是否有授权。
- public class AuthorityInterceptor extends MethodFilterInterceptor {
- private static final long serialVersionUID = -1070565846576510701L;
- @Override
- protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
- User currentUser = SpringSecurityUtils.getCurrentUser();
- if (currentUser != null) {
- try {
- Object action = actionInvocation.getAction();
- ActionProxy actionProxy = actionInvocation.getProxy();
- Method method = action.getClass().getMethod(actionProxy.getMethod());
- ActionRoles actionRoles = method.getAnnotation(ActionRoles.class);
- if (actionRoles != null) {
- boolean authorized = false;
- SysRole[] sysRoles = actionRoles.value();
- for (SysRole sysRole : sysRoles) {
- if (SysRole.equals(currentUser.getRoleName(), sysRole)) {
- authorized = true;
- break;
- }
- }
- if (!authorized) {
- return BaseActionSupport.UNAUTHORIZED;
- }
- }
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- }
- }
- return actionInvocation.invoke();
- }
- }
public class AuthorityInterceptor extends MethodFilterInterceptor {
private static final long serialVersionUID = -1070565846576510701L;
@Override
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
User currentUser = SpringSecurityUtils.getCurrentUser();
if (currentUser != null) {
try {
Object action = actionInvocation.getAction();
ActionProxy actionProxy = actionInvocation.getProxy();
Method method = action.getClass().getMethod(actionProxy.getMethod());
ActionRoles actionRoles = method.getAnnotation(ActionRoles.class);
if (actionRoles != null) {
boolean authorized = false;
SysRole[] sysRoles = actionRoles.value();
for (SysRole sysRole : sysRoles) {
if (SysRole.equals(currentUser.getRoleName(), sysRole)) {
authorized = true;
break;
}
}
if (!authorized) {
return BaseActionSupport.UNAUTHORIZED;
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
return actionInvocation.invoke();
}
}
最后一步,很简单啦,在Struts2的Action方法上面加上注解。
- @ActionRoles({SysRole.ROLE_ADMIN, SysRole.ROLE_USER})
- public String execute() throws Exception {
- return SUCCESS;
- }
@ActionRoles({SysRole.ROLE_ADMIN, SysRole.ROLE_USER})
public String execute() throws Exception {
return SUCCESS;
}
到此,大功告成。还需要简单的配置一下拦截器,这个就不写了。还要注意,需要有一个全局结果页面,用以展示“未经授权”。我的BaseActionSupport.UNAUTHORIZED是转跳到403.jsp页面的。