Wicket Authorization with Spring Security Filter Chain,重定向循环



我想用Wickets@AuthorizationInstantiation保护我的页面,应用程序应该受到Spring Security Filter Chain的保护。

我在网上找到了一些例子,但没有一个与我想要实现应用程序的方式相匹配。

我想在我的Wicket页面中使用类似@AuthorizeInstantiation({"user", "admin"})的东西,稍后使用其他Wicket特定的方式来授权我的应用程序。

当我将应用程序部署到Tomcat并调用浏览器中的登录页面(或其他页面(时,会发生无休止的重定向

我想MySecurityConfigurerAdapter#configure(HttpSecurity http)有问题

我的AuthenticatedWebSession实现中的方法authenticate(username, password)getRoles()不是由Wicket调用的。

下面是我的代码摘录。完整的MCVE可在Gitlab 上获得

WebSecurityConfigurerAdapter的实现

@Configuration
@EnableWebSecurity
public class MySecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws
Exception {
authenticationManagerBuilder.userDetailsService(new MyUserDetailsService());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//@formatter:off
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/favicon.ico").permitAll()
.antMatchers("/logout_success").permitAll()
.antMatchers("login").permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.loginProcessingUrl("/login")
.and()
.addFilter(new SecurityContextPersistenceFilter()).securityContext();
//@formatter:on
}
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
}

UserDetailsService的实现

public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<MyAuthority> authorities = new HashSet<>();
MyAuthority admin = new MyAuthority("admin");
MyAuthority user = new MyAuthority("user");
MyAuthority guest = new MyAuthority("guest");
switch (username) {
case "tim":
case "steve":
authorities.add(admin);
authorities.add(user);
authorities.add(guest);
break;
case "craig":
authorities.add(user);
authorities.add(guest);
break;
case "phil":
authorities.add(guest);
break;
default:
throw new UsernameNotFoundException("user "" + username + "" unknown");
}
return new User(username, "123456", authorities);
}
class MyAuthority implements GrantedAuthority {
private final String role;
MyAuthority(String r) {
role = r;
}
@Override
public String getAuthority() {
return role;
}
}
}

AbstractSecurityWebApplicationInitializer的实现

public class MySecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer
{
public MySecurityWebApplicationInitializer()
{
super(MySecurityConfigurerAdapter.class);
}
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext)
{
super.beforeSpringSecurityFilterChain(servletContext);
}
@Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext)
{
super.afterSpringSecurityFilterChain(servletContext);
Filter myWicketFilter=new WicketFilter(new MyApplication())
{
@Override
public void init(boolean isServlet, FilterConfig filterConfig) throws ServletException
{
setFilterPath("");
super.init(isServlet, filterConfig);
}
};
FilterRegistration.Dynamic wicketRegistration;
wicketRegistration = servletContext.addFilter("myWicketFilter", myWicketFilter);
wicketRegistration.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "*");
}
}

Wicket应用

public class MyApplication extends AuthenticatedWebApplication {
@Override
protected void init() {
super.init();
getComponentInstantiationListeners().add(new SpringComponentInjector(this));
getSecuritySettings().setAuthorizationStrategy(new AnnotationsRoleAuthorizationStrategy(this));
mountPage("/login", Login.class);
mountPage("/start", UserStart.class);
mountPage("/public", Public.class);
mountPage("/admin", Admin.class);
}
@Override
protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() {
return MyWebSession.class;
}
@Override
protected Class<? extends WebPage> getSignInPageClass() {
return Login.class;
}
@Override
public Class<? extends Page> getHomePage() {
return UserStart.class;
}
}

AuthenticatedWebSession的实现

public class MyWebSession extends AuthenticatedWebSession {
@SpringBean
private
AuthenticationManager authenticationManager;
public MyWebSession(Request request) {
super(request);
Injector.get().inject(this);
}
@Override
protected boolean authenticate(String username, String password) {
boolean authenticated;
try {
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username
, password));
SecurityContextHolder.getContext().setAuthentication(authentication);
authenticated = authentication.isAuthenticated();
System.out.println("Authentication: "+authenticated+", User ""+username+""");
}
catch (AuthenticationException e) {
authenticated = false;
System.err.println("Login mit "" + username + "" fehlgeschlagen, " + e.getMessage());
}
return authenticated;
}
@Override
public Roles getRoles() {
Roles roles = new Roles();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.isAuthenticated()) {
for (GrantedAuthority authority : authentication.getAuthorities()) {
roles.add(authority.getAuthority());
}
}
System.out.println("Roles: "+roles.toString());
return roles;
}
}

更新

我在.antMatchers("login").permitAll()中插入了一个缺失的/,但它没有帮助。

当我调用http://localhost:8080/context_name/login时,我会看到登录页面。在提交表格时,会出现StackOverflowError:

java.lang.StackOverflowError
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:163)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:494)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:494)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:494)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:494)

这是Spring Security调试日志的摘录:

********************************************************************
**********        Security debugging is enabled.       *************
**********    This may include sensitive information.  *************
**********      Do not use in a production system!     *************
********************************************************************

23-Jul-2018 16:42:40.561 INFO [RMI TCP Connection(3)-127.0.0.1] org.springframework.web.context.ContextLoader.initWebApplicationContext Root WebApplicationContext: initialization completed in 936 ms
[RMI TCP Connection(3)-127.0.0.1] INFO org.apache.wicket.Application - [myWicketFilter] init: Wicket core library initializer
[RMI TCP Connection(3)-127.0.0.1] INFO org.apache.wicket.Application - [myWicketFilter] init: Wicket extensions initializer
[RMI TCP Connection(3)-127.0.0.1] INFO org.apache.wicket.protocol.http.WebApplication - [myWicketFilter] Started Wicket version 8.0.0 in DEVELOPMENT mode
********************************************************************
*** WARNING: Wicket is running in DEVELOPMENT mode.              ***
***                               ^^^^^^^^^^^                    ***
*** Do NOT deploy to your live server(s) without changing this.  ***
*** See Application#getConfigurationType() for more information. ***
********************************************************************
[2018-07-23 04:42:40,779] Artifact wicketAuthSpringSec:war: Artifact is deployed successfully
[2018-07-23 04:42:40,779] Artifact wicketAuthSpringSec:war: Deploy took 3.465 milliseconds
23-Jul-2018 16:42:40.951 INFO [http-apr-8080-exec-4] Spring Security Debugger.info 
************************************************************
Request received for GET '/':
org.apache.catalina.connector.RequestFacade@16ec26d8
servletPath:/
pathInfo:null
headers: 
user-agent: IntelliJ IDEA/181.5281.24
accept-encoding: gzip
cache-control: no-cache
pragma: no-cache
host: localhost:8080
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
connection: keep-alive

Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]

************************************************************

23-Jul-2018 16:42:41.013 INFO [http-apr-8080-exec-6] Spring Security Debugger.info 
************************************************************
Request received for GET '/start':
org.apache.catalina.connector.RequestFacade@16ec26d8
servletPath:/start
pathInfo:null
headers: 
user-agent: IntelliJ IDEA/181.5281.24
accept-encoding: gzip
cache-control: no-cache
pragma: no-cache
host: localhost:8080
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
connection: keep-alive

Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]

************************************************************

Roles: ROLE_ANONYMOUS
23-Jul-2018 16:42:41.075 INFO [http-apr-8080-exec-6] Spring Security Debugger.info 
************************************************************
New HTTP session created: 547244D1E42D71DA11F9BAD666EBFC3F
Call stack: 
at org.springframework.security.web.debug.Logger.info(Logger.java:44)
at org.springframework.security.web.debug.DebugRequestWrapper.getSession(DebugFilter.java:166)
at org.springframework.security.web.debug.DebugRequestWrapper.getSession(DebugFilter.java:177)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231)
at org.apache.wicket.session.HttpSessionStore.getHttpSession(HttpSessionStore.java:85)
at org.apache.wicket.session.HttpSessionStore.getSessionId(HttpSessionStore.java:146)
at org.apache.wicket.Session.bind(Session.java:270)
at org.apache.wicket.RestartResponseAtInterceptPageException$InterceptData.set(RestartResponseAtInterceptPageException.java:140)
at org.apache.wicket.RestartResponseAtInterceptPageException.<init>(RestartResponseAtInterceptPageException.java:82)
at org.apache.wicket.RestartResponseAtInterceptPageException.<init>(RestartResponseAtInterceptPageException.java:68)
at org.apache.wicket.authroles.authentication.AuthenticatedWebApplication.restartResponseAtSignInPage(AuthenticatedWebApplication.java:103)
at org.apache.wicket.authroles.authentication.AuthenticatedWebApplication.onUnauthorizedInstantiation(AuthenticatedWebApplication.java:81)
at org.apache.wicket.Application$1.onInstantiation(Application.java:279)
at org.apache.wicket.application.ComponentInstantiationListenerCollection$1.notify(ComponentInstantiationListenerCollection.java:38)
at org.apache.wicket.application.ComponentInstantiationListenerCollection$1.notify(ComponentInstantiationListenerCollection.java:34)
at org.apache.wicket.util.listener.ListenerCollection.notify(ListenerCollection.java:80)
at org.apache.wicket.application.ComponentInstantiationListenerCollection.onInstantiation(ComponentInstantiationListenerCollection.java:33)
at org.apache.wicket.Component.<init>(Component.java:679)
at org.apache.wicket.MarkupContainer.<init>(MarkupContainer.java:178)
at org.apache.wicket.Page.<init>(Page.java:171)
at org.apache.wicket.Page.<init>(Page.java:135)
at org.apache.wicket.markup.html.WebPage.<init>(WebPage.java:74)
at de.example.app.pages.UserStart.<init>(UserStart.java:10)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:175)
at org.apache.wicket.session.DefaultPageFactory.newPage(DefaultPageFactory.java:67)
at org.apache.wicket.DefaultMapperContext.newPageInstance(DefaultMapperContext.java:90)
at org.apache.wicket.core.request.handler.PageProvider$Provision.getPage(PageProvider.java:380)
at org.apache.wicket.core.request.handler.PageProvider.getPageInstance(PageProvider.java:171)
at org.apache.wicket.request.handler.render.PageRenderer.getPage(PageRenderer.java:78)
at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:231)
at org.apache.wicket.core.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:202)
at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:912)
at org.apache.wicket.request.RequestHandlerExecutor.execute(RequestHandlerExecutor.java:65)
at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:283)
at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:253)
at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:221)
at org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:262)
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:204)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:286)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
at org.springframework.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:90)
at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:77)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2508)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2497)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

************************************************************

23-Jul-2018 16:42:41.091 INFO [http-apr-8080-exec-8] Spring Security Debugger.info 
************************************************************
Request received for GET '/login':
org.apache.catalina.connector.RequestFacade@16ec26d8
servletPath:/login
pathInfo:null
headers: 
user-agent: IntelliJ IDEA/181.5281.24
accept-encoding: gzip
cache-control: no-cache
pragma: no-cache
host: localhost:8080
accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
connection: keep-alive

Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]

************************************************************

23-Jul-2018 16:42:41.216 INFO [http-apr-8080-exec-8] Spring Security Debugger.info 
************************************************************
New HTTP session created: 55EDC4A7C5B989B253923455CE54965A

当我在Spring WebSecurityConfigurerAdapter中发现StackOverflowError Trying Expose-AuthenticationManager时,我解决了核心问题。

无限循环是由覆盖authenticationManager()而不是authenticationManagerBean()引起的。与userDetailsServiceBean()相同。

public class MySecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired(name="userDetailsService")
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws
Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
....
}
@Bean(name="authenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/* not working:
public AuthenticationManager authenticationManager() {  ...  }
*/
@Bean(name="userDetailsService")
@Override
public UserDetailsService userDetailsServiceBean() {
return new MyUserDetailsService();
}
// not working:  UserDetailsService userDetailsService() { ... }
}

最新更新