首先,我最诚挚地对这个问题道歉,但我真的不知道如何缩短它,因为每个部分都是一个特例。不可否认,我可能会对此视而不见,因为我现在正撞在墙上几天,我开始变得绝望了。
我非常尊重和感谢所有读过它的人。
我希望能够使用Shiro's AuthenticationException将JAX-RS Responses及其子类映射到Jersey ExceptionMappers,使用创建嵌入式的Guice 3.0 Injector进行设置码头。
使用Guice Injector
创建嵌入式Jetty// imports omitted for brevity
public class Bootstrap {
public static void main(String[] args) throws Exception {
/*
* The ShiroWebModule is passed as a class
* since it needs a ServletContext to be initialized
*/
Injector injector = Guice.createInjector(new ServerModule(MyShiroWebModule.class));
Server server = injector.getInstance(Server.class);
server.start();
server.join();
}
}
ServerModule
绑定Jetty服务器的提供程序:
public class ServerModule extends AbstractModule {
Class<? extends ShiroWebModule> clazz;
public ServerModule(Class <?extends ShiroWebModule> clazz) {
this.clazz = clazz;
}
@Override
protected void configure() {
bind(Server.class)
.toProvider(JettyProvider.withShiroWebModule(clazz))
.in(Singleton.class);
}
}
JettyProvider
设置Jetty WebApplicationContext,注册Guice所需的ServletContextListener以及更多内容,我留下来确保没有&#34;副作用&#34;可能是隐藏的:
public class JettyProvider implements Provider<Server>{
@Inject
Injector injector;
@Inject
@Named("server.Port")
Integer port;
@Inject
@Named("server.Host")
String host;
private Class<? extends ShiroWebModule> clazz;
private static Server server;
private JettyProvider(Class<? extends ShiroWebModule> clazz){
this.clazz = clazz;
}
public static JettyProvider withShiroWebModule(Class<? extends ShiroWebModule> clazz){
return new JettyProvider(clazz);
}
public Server get() {
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/");
// Set during testing only
webAppContext.setResourceBase("src/main/webapp/");
webAppContext.setParentLoaderPriority(true);
webAppContext.addEventListener(
new MyServletContextListener(injector,clazz)
);
webAppContext.addFilter(
GuiceFilter.class, "/*",
EnumSet.allOf(DispatcherType.class)
);
webAppContext.setThrowUnavailableOnStartupException(true);
QueuedThreadPool threadPool = new QueuedThreadPool(500, 10);
server = new Server(threadPool);
ServerConnector connector = new ServerConnector(server);
connector.setHost(this.host);
connector.setPort(this.port);
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(new NCSARequestLog());
HandlerCollection handlers = new HandlerCollection(true);
handlers.addHandler(webAppContext);
handlers.addHandler(requestLogHandler);
server.addConnector(connector);
server.setStopAtShutdown(true);
server.setHandler(handlers);
return server;
}
}
在MyServletContextListener
中,我创建了一个子注入器,它使用JerseyServletModule初始化:
public class MyServletContextListener extends GuiceServletContextListener {
private ServletContext servletContext;
private Injector injector;
private Class<? extends ShiroWebModule> shiroModuleClass;
private ShiroWebModule module;
public ServletContextListener(Injector injector,
Class<? extends ShiroWebModule> clazz) {
this.injector = injector;
this.shiroModuleClass = clazz;
}
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
this.servletContext = servletContextEvent.getServletContext();
super.contextInitialized(servletContextEvent);
}
@Override
protected Injector getInjector() {
/*
* Since we finally have our ServletContext
* we can now instantiate our ShiroWebModule
*/
try {
module = shiroModuleClass.getConstructor(ServletContext.class)
.newInstance(this.servletContext);
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
/*
* Now, we create a child injector with the JerseyModule
*/
Injector child = injector.createChildInjector(module,
new JerseyModule());
SecurityManager securityManager = child
.getInstance(SecurityManager.class);
SecurityUtils.setSecurityManager(securityManager);
return child;
}
}
JerseyModule是JerseyServletModule
的子类,现在把所有东西放在一起:
public class JerseyModule extends JerseyServletModule {
@Override
protected void configureServlets() {
bindings();
filters();
}
private void bindings() {
bind(DefaultServlet.class).asEagerSingleton();
bind(GuiceContainer.class).asEagerSingleton();
serve("/*").with(DefaultServlet.class);
}
private void filters() {
Map<String, String> params = new HashMap<String, String>();
// Make sure Jersey scans the package
params.put("com.sun.jersey.config.property.packages",
"com.example.webapp");
params.put("com.sun.jersey.config.feature.Trace", "true");
filter("/*").through(GuiceShiroFilter.class,params);
filter("/*").through(GuiceContainer.class, params);
/*
* Although the ExceptionHandler is already found by Jersey
* I bound it manually to be sure
*/
bind(ExceptionHandler.class);
bind(MyService.class);
}
}
ExceptionHandler
非常简单,看起来像这样:
@Provider
@Singleton
public class ExceptionHandler implements
ExceptionMapper<AuthenticationException> {
public Response toResponse(AuthenticationException exception) {
return Response
.status(Status.UNAUTHORIZED)
.entity("auth exception handled")
.build();
}
}
现在,当我想访问受限资源并输入正确的主体/凭证组合时,一切正常。但是一旦输入不存在的用户或错误的密码,我希望Shiro抛出AuthenticationException
,我希望它由上面的ExceptionHandler
处理。
在开头使用Shiro提供的默认AUTHC
过滤器时,我注意到.NETExceptions被静默吞下,用户再次被重定向到登录页面。
所以我将Shiro的FormAuthenticationFilter子类化为AuthenticationException
如果有的话:
public class MyFormAutheticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onLoginFailure(AuthenticationToken token,
AuthenticationException e, ServletRequest request,
ServletResponse response) {
if(e != null){
throw e;
}
return super.onLoginFailure(token, e, request, response);
}
}
我也尝试将e
包裹在MappableContainerException中。{/ p>
这两种方法都会导致同样的问题:不是由定义的ExceptionHandler
处理异常,而是抛出javax.servlet.ServletException
:
javax.servlet.ServletException: org.apache.shiro.authc.AuthenticationException: Unknown Account!
at org.apache.shiro.web.servlet.AdviceFilter.cleanup(AdviceFilter.java:196)
at org.apache.shiro.web.filter.authc.AuthenticatingFilter.cleanup(AuthenticatingFilter.java:155)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:148)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.shiro.guice.web.SimpleFilterChain.doFilter(SimpleFilterChain.java:41)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118)
at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:113)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:110)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:499)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
at java.lang.Thread.run(Thread.java:744)
Caused by: org.apache.shiro.authc.AuthenticationException: Unknown Account!
at com.example.webapp.security.MyAuthorizingRealm.doGetAuthenticationInfo(MyAuthorizingRealm.java:27)
at org.apache.shiro.realm.AuthenticatingRealm.getAuthenticationInfo(AuthenticatingRealm.java:568)
at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:180)
at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267)
at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
at org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106)
at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:270)
at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:256)
at org.apache.shiro.web.filter.authc.AuthenticatingFilter.executeLogin(AuthenticatingFilter.java:53)
at org.apache.shiro.web.filter.authc.FormAuthenticationFilter.onAccessDenied(FormAuthenticationFilter.java:154)
at org.apache.shiro.web.filter.AccessControlFilter.onAccessDenied(AccessControlFilter.java:133)
at org.apache.shiro.web.filter.AccessControlFilter.onPreHandle(AccessControlFilter.java:162)
at org.apache.shiro.web.filter.PathMatchingFilter.isFilterChainContinued(PathMatchingFilter.java:203)
at org.apache.shiro.web.filter.PathMatchingFilter.preHandle(PathMatchingFilter.java:178)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:131)
... 32 more
鉴于环境无法改变,我如何才能实现仍然可以通过Guice请求服务器实例,而Shiro的例外是通过Jersey自动发现的ExceptionMappers来处理的?< / p>
答案 0 :(得分:4)
这个问题对我来说太复杂了,但是我发现了一个问题,我认为是答案,如果我结果是这样的话,我会删除这个答案。错。
你这样做:
@Provider
@Singleton
public class ExceptionHandler implements
ExceptionMapper<AuthenticationException> {
哪个是正确的,您应该与this question中的这两个注释绑定。但是,你做的不同的是:
/*
* Although the ExceptionHandler is already found by Jersey
* I bound it manually to be sure
*/
bind(ExceptionHandler.class);
类定义中的注释具有较低优先级,而不是模块的configure()
方法中的注释,这意味着当您使用删除注释时绑定&#34;它只是为了确保&#34;。尝试删除该行代码,看看是否能解决您的问题。如果它没有解决问题,那么无论如何都要将其删除,因为我确信它至少是问题的一部分 - 该语句会删除那些必要的注释。
答案 1 :(得分:0)
我也没有办法做到这一点。在身份验证期间,看起来Jersey过滤器/处理程序在Shiro servlet堆栈上不活动。作为AuthenticationException的解决方法,我选择覆盖AuthenticatingFilter上的AdviceFilter :: cleanup(...)方法并直接返回自定义消息。
public class MyTokenAuthenticatingFilter extends AuthenticatingFilter {
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
// regular auth/token creation
}
@Override
protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) throws ServletException, IOException {
HttpServletResponse httpResponse = (HttpServletResponse)response;
if ( null != existing ) {
httpResponse.setContentType(MediaType.APPLICATION_JSON);
httpResponse.getOutputStream().write(String.format("{\"error\":\"%s\"}", existing.getMessage()).getBytes());
httpResponse.setStatus(Response.Status.FORBIDDEN.getStatusCode());
existing = null; // prevent Shiro from tossing a ServletException
}
super.cleanup(request, httpResponse, existing);
}
}
当身份验证成功时,ExceptionMappers可以很好地处理Jersey控制器上下文中抛出的异常。