问题:JavaEE6的实现在哪里?
我目前正在开发一个JavaEE6项目,我发现虽然我已经根据文档配置了web.xml和shiro.ini,但Shiro的注释并没有开箱即用。
这就是我所拥有的:
1。)页面:
<h:form>
<h:commandLink action="#{userBean.action1()}" value="Action 1"></h:commandLink>
</h:form>
2。)支持bean:
@Stateless
@Named
public class UserBean {
@Inject
private Logger log;
@RequiresAuthentication
public void action1() {
log.debug("action.1");
}
}
3。)web.xml
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4。)shiro.ini
[main]
# listener = org.apache.shiro.config.event.LoggingBeanListener
shiro.loginUrl = /login.xhtml
[users]
# format: username = password, role1, role2, ..., roleN
root = secret,admin
guest = guest,guest
presidentskroob = 12345,president
darkhelmet = ludicrousspeed,darklord,schwartz
lonestarr = vespa,goodguy,schwartz
[roles]
# format: roleName = permission1, permission2, ..., permissionN
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
[urls]
# The /login.jsp is not restricted to authenticated users (otherwise no one could log in!), but
# the 'authc' filter must still be specified for it so it can process that url's
# login submissions. It is 'smart' enough to allow those requests through as specified by the
# shiro.loginUrl above.
/login.xhtml = authc
/logout = logout
/account/** = authc
/remoting/** = authc, roles[b2bClient], perms["remote:invoke:lan,wan"]
但是当我点击按钮时,它仍然会执行操作。它应该抛出unauthorizede异常吗?其他shiro注释也是如此。
请注意,如果我手动执行检查,则可以正常运行:
public void action1() {
Subject currentUser = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken("guest", "guest");
currentUser.login(token);
log.debug("user." + currentUser);
if (currentUser.isAuthenticated()) {
log.debug("action.1");
} else {
log.debug("not authenticated");
}
}
谢谢,
czetsuya
答案 0 :(得分:4)
您基本上需要Java EE interceptor才能扫描调用的CDI和EJB方法上的注释。
首先创建一个拦截器必须拦截的注释:
@Inherited
@InterceptorBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ShiroSecured {
//
}
然后创建拦截器本身:
@Interceptor
@ShiroSecured
public class ShiroSecuredInterceptor implements Serializable {
private static final long serialVersionUID = 1L;
@AroundInvoke
public Object interceptShiroSecurity(InvocationContext context) throws Exception {
Class<?> c = context.getTarget().getClass();
Method m = context.getMethod();
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated() && hasAnnotation(c, m, RequiresAuthentication.class)) {
throw new UnauthenticatedException("Authentication required");
}
if (subject.getPrincipal() != null && hasAnnotation(c, m, RequiresGuest.class)) {
throw new UnauthenticatedException("Guest required");
}
if (subject.getPrincipal() == null && hasAnnotation(c, m, RequiresUser.class)) {
throw new UnauthenticatedException("User required");
}
RequiresRoles roles = getAnnotation(c, m, RequiresRoles.class);
if (roles != null) {
subject.checkRoles(Arrays.asList(roles.value()));
}
RequiresPermissions permissions = getAnnotation(c, m, RequiresPermissions.class);
if (permissions != null) {
subject.checkPermissions(permissions.value());
}
return context.proceed();
}
private static boolean hasAnnotation(Class<?> c, Method m, Class<? extends Annotation> a) {
return m.isAnnotationPresent(a)
|| c.isAnnotationPresent(a)
|| c.getSuperclass().isAnnotationPresent(a);
}
private static <A extends Annotation> A getAnnotation(Class<?> c, Method m, Class<A> a) {
return m.isAnnotationPresent(a) ? m.getAnnotation(a)
: c.isAnnotationPresent(a) ? c.getAnnotation(a)
: c.getSuperclass().getAnnotation(a);
}
}
请注意,在目标类的超类上检查注释,如果CDI实际上是一个代理,并且Shiro的注释没有设置@Inherited
,则目标类可能会被检查。
为了让它在CDI托管bean上运行,首先在/WEB-INF/beans.xml
中注册拦截器,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd"
>
<interceptors>
<class>com.example.interceptor.ShiroSecuredInterceptor</class>
</interceptors>
</beans>
类似地,为了使它能够在EJB上工作,首先在/WEB-INF/ejb-jar.xml
中注册拦截器,如下所示(如果你在EAR中有一个单独的EJB项目,则在/META-INF/ejb-jar.xml
中注册):
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
>
<interceptors>
<interceptor>
<interceptor-class>com.example.interceptor.ShiroSecuredInterceptor</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>com.example.interceptor.ShiroSecuredInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
在CDI托管bean上,您需要设置自定义@ShiroSecured
注释以使拦截器运行。
@Named
@RequestScoped
@ShiroSecured
public class SomeBean {
@RequiresRoles("ADMIN")
public void doSomethingWhichIsOnlyAllowedByADMIN() {
// ...
}
}
这在EJB上不是必需的,ejb-jar.xml
已经在所有EJB上注册了它。
答案 1 :(得分:-2)
基本上,我缺少的是Shiro的Requires *接口的实现,所以我根据我的需要实现。对于那些感兴趣的人,您可以在此处找到它:http://czetsuya-tech.blogspot.com/2012/10/how-to-integrate-apache-shiro-with.html