我有一个半工作(如在runnable中)Spring Boot网络应用程序,我正在尝试添加安全性。我有OAuth2的REST服务,但我也需要在WebMvc端进行身份验证。我无法按照我的方式工作,所以我重构了配置:
public class SpringConfigurationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
@Override
protected Class<?>[] getRootConfigClasses()
{
return new Class[] { AppConfiguration.class };
}
@Override
protected Class<?>[] getServletConfigClasses()
{
return null;
}
@Override
protected String[] getServletMappings()
{
return new String[] { "/" };
}
}
和
@Configuration
@ComponentScan(basePackages = {"co.sens.rest", "co.sens.data", "co.sens.docdata", "co.sens.aggregators"})
@Import({ WebMvcConfig.class, OAuth2ServerConfig.class, SecurityConfiguration.class, CustomUserDetailsService.class })
public class AppConfiguration {
}
因此,目的是扫描应用程序本身(co.sens.rest)中的组件以及引用的jar中的外部代码。保持数据访问分离的主要目的。
由于这样做,我现在收到以下错误,我可以收集到的是因为Spring Security很快就开始了。它正在尝试将我的UserRepository自动装配到我的自定义UserDetailsService。
在添加AbstractAnnotationConfigDispatcherServletInitializer之前,我可以让它运行但我无法在网络上获得安全性
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'embeddedServletContainerCustomizerBeanPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private co.sens.rest.config.SecurityConfiguration co.sens.rest.config.MethodSecurityConfig.securityConfig; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.setContentNegotationStrategy(org.springframework.web.accept.ContentNegotiationStrategy); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration.setConfigurers(java.util.List); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private co.sens.rest.controllers.UsersController co.sens.rest.controllers.web.WebController.usersController; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'usersController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private co.sens.data.Operations co.sens.rest.controllers.UsersController.dataOperations; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'operations': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private co.sens.docdata.DocStoreOperations co.sens.data.Operations.docStoreOperations; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'docStoreOperations': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private co.sens.docdata.repositories.UserRepository co.sens.docdata.DocStoreOperations.userRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Invocation of init method failed; nested exception is java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@553a3d88: startup date [Fri Jul 10 08:55:21 BST 2015]; root of context hierarchy
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:232)
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:615)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:465)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at co.sens.rest.Application.main(Application.java:11)
完整的堆栈跟踪:http://pastebin.com/SJLa2pSc
更新
配置:
package co.sens.rest.config;
@Configuration
@ComponentScan(basePackages = {"co.sens.data", "co.sens.docdata", "co.sens.aggregators"})
public class AppConfiguration {
}
public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
@Configuration
public class OAuth2ServerConfig {
private static final String SENS_RESOURCE_ID = "sens";
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(SENS_RESOURCE_ID).stateless(false); //.authenticationEntryPoint(new RestAuthenticationEntryPoint()); //.stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api").access("#oauth2.hasScope('read') and hasRole('ROLE_USER')");
}
}
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
private UserApprovalHandler userApprovalHandler;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("sensapp")
.resourceIds(SENS_RESOURCE_ID)
.authorizedGrantTypes("authorization_code", "refresh_token",
"password")
.authorities("USER")
.scopes("read", "write")
.secret("secret");
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm("sens/client");
}
}
protected static class Approvals {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private TokenStore tokenStore;
@Bean
public ApprovalStore approvalStore() throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
@Bean
@Lazy
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public SensUserApprovalHandler userApprovalHandler() throws Exception {
SensUserApprovalHandler handler = new SensUserApprovalHandler();
handler.setApprovalStore(approvalStore());
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
handler.setUseApprovalStore(true);
return handler;
}
}
}
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/web/**").authenticated()
.antMatchers("/login/**", "/resources/**").permitAll()
.and()
.formLogin().loginProcessingUrl("/login").failureUrl("/login?authorization_error=true").defaultSuccessUrl("/web/home").loginPage("/login").permitAll()
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login")
.permitAll();
}
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
public class ServletInitializer extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.scan(ClassUtils.getPackageName(getClass()));
return context;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*");
}
}
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
@Bean
public TemplateResolver defaultTemplateResolver() {
TemplateResolver result = new ServletContextTemplateResolver();
result.setPrefix("/WEB-INF/templates/");
result.setSuffix(".html");
result.setTemplateMode("LEGACYHTML5");
result.setCacheable(false); // TODO Only for dev
return result;
}
@Bean
public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
Set<IDialect> dialects = new HashSet<>();
dialects.add(new SpringSecurityDialect());
templateEngine.setAdditionalDialects(dialects);
return templateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
@Bean
public ContentNegotiatingViewResolver contentViewResolver() throws Exception {
ContentNegotiationManagerFactoryBean contentNegotiationManager = new ContentNegotiationManagerFactoryBean();
contentNegotiationManager.addMediaType("json", MediaType.APPLICATION_JSON);
MappingJackson2JsonView defaultView = new MappingJackson2JsonView();
defaultView.setExtractValueFromSingleKeyModel(true);
ContentNegotiatingViewResolver contentViewResolver = new ContentNegotiatingViewResolver();
contentViewResolver.setContentNegotiationManager(contentNegotiationManager.getObject());
contentViewResolver.setDefaultViews(Arrays.<View>asList(defaultView));
return contentViewResolver;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
安全调试日志
2015-07-14 14:05:31.352 INFO 12373 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2015-07-14 14:05:31.352 INFO 12373 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2015-07-14 14:05:31.387 INFO 12373 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 35 ms
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/token']
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/web/home'; against '/oauth/token'
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/token_key']
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/web/home'; against '/oauth/token_key'
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/check_token']
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/web/home'; against '/oauth/check_token'
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2015-07-14 14:05:31.460 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration$NotOAuthRequestMatcher@7d97df04
2015-07-14 14:05:31.461 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.web.util.matcher.OrRequestMatcher : matched
2015-07-14 14:05:31.462 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2015-07-14 14:05:31.462 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2015-07-14 14:05:31.463 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2015-07-14 14:05:31.463 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@63d700f9
2015-07-14 14:05:31.463 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2015-07-14 14:05:31.463 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/web/home'; against '/logout'
2015-07-14 14:05:31.464 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
2015-07-14 14:05:31.464 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.o.p.a.BearerTokenExtractor : Token not found in headers. Trying request parameters.
2015-07-14 14:05:31.464 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.o.p.a.BearerTokenExtractor : Token not found in request parameters. Not an OAuth2 request.
2015-07-14 14:05:31.464 DEBUG 12373 --- [nio-8080-exec-1] p.a.OAuth2AuthenticationProcessingFilter : No token in request, will continue chain.
2015-07-14 14:05:31.464 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2015-07-14 14:05:31.464 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2015-07-14 14:05:31.465 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2015-07-14 14:05:31.466 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2015-07-14 14:05:31.467 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2015-07-14 14:05:31.467 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.session.SessionManagementFilter : Requested session ID 09564785AF0DDD41AB2645CEEE04E79E is invalid.
2015-07-14 14:05:31.467 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2015-07-14 14:05:31.467 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2015-07-14 14:05:31.467 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/web/home'; against '/api'
2015-07-14 14:05:31.467 DEBUG 12373 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Public object - authentication not attempted
2015-07-14 14:05:31.468 DEBUG 12373 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /web/home reached end of additional filter chain; proceeding with original chain
2015-07-14 14:05:31.622 ERROR 12373 --- [nio-8080-exec-1] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8080-exec-1] Exception processing template "index": Error retrieving value for property "username" of authentication object of class org.springframework.security.authentication.AnonymousAuthenticationToken (index)
2015-07-14 14:05:31.624 DEBUG 12373 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2015-07-14 14:05:31.626 ERROR 12373 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error retrieving value for property "username" of authentication object of class org.springframework.security.authentication.AnonymousAuthenticationToken (index)] with root cause
答案 0 :(得分:1)
如果您使用的是Spring Boot,则应移除SpringConfigurationInitializer
并使用SpringBootServletInitializer
。例如:
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
有关详细信息,请参阅Spring Boot参考。