我正在研究一个项目,该项目需要从具有XML配置的多模块Spring升级到具有Java配置的Spring Boot Monolith。
除了将OAuth2 XML配置转换为Java配置以外,其他所有方法似乎都正常运行。
应用程序部署良好,身份验证也可以,但是拒绝访问Java控制器具有使用@Secured({GroupRoleNames.ROLE_ACCOUNT_APPROVER})注释的方法的页面,请参阅下面的stacktrace。无法解决,但我可以肯定我的OAuth2配置是问题
将感谢您对我哪里出错了的任何建议。
<mvc:annotation-driven/>
<!-- ========================= OAUTH TOKEN VENDING ===================================== -->
<!--TOKEN REQUEST -->
<security:http pattern="/oauth/token" use-expressions="true" create-session="stateless"
entry-point-ref="clientAuthenticationEntryPoint"
authentication-manager-ref="clientAuthenticationManager">
<security:intercept-url method="POST" pattern="/oauth/token" access="permitAll" />
<security:anonymous enabled="true" />
<security:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<security:access-denied-handler ref="oauthAccessDeniedHandler" />
</security:http>
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserService" />
</security:authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<!-- Defines just the single password grant type client -->
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="${oauth.client.id}" secret="${oauth.client.secret}" authorized-grant-types="password, refresh_token" authorities="API_TOKEN_REQUESTOR" scope="read,write,trust" access-token-validity="3600" />
</oauth:client-details-service>
<!-- If authentication fails and the caller has asked for a specific content type response, this entry point can send one, along with a standard 401 status-->
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="MyCompany_API/client" />
<property name="typeName" value="Basic" />
</bean>
<!-- If authorization fails and the caller has asked for a specific content type response, this entry point can send one, along with a standard 403 status -->
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<!-- A filter and authentication endpoint for the OAuth2 Token Endpoint.
Allows clients to authenticate using request parameters if included as a security filter, as permitted by the specification (but not recommended).
It is recommended by the specification that you permit HTTP basic authentication for clients, and not use this filter at all.
POSSIBLY NEED TO OVERRIDE THIS FILTER AND IMPLEMENT OWN AUTHENTICATION CODE.
-->
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<!-- ========================= OAUTH RESOURCE PROTECTION ===================================== -->
<security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" access-decision-manager-ref="accessDecisionManager"/>
<security:http create-session="stateless" use-expressions="true" entry-point-ref="mycompanyAuthenticationExceptionHandler" access-decision-manager-ref="accessDecisionManager">
<security:anonymous enabled="false" />
<security:custom-filter ref="preAuthFilter" before="FIRST" />
<security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<security:custom-filter ref="updateSavedRequestFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:access-denied-handler ref="mycompanyAuthorisationExceptionHandler" />
</security:http>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter" />
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter ">
<property name="rolePrefix" value="" />
</bean>
<bean id="preAuthFilter" class="uk.co.mycompany.api.auth.PreAuthFilter"/>
<bean id="updateSavedRequestFilter" class="uk.co.mycompany.api.auth.UpdateSavedRequestFilter"/>
<bean id="mycompanyAuthenticationExceptionHandler" class="uk.co.mycompany.api.auth.MyCompanyAuthenticationExceptionHandler">
<property name="realmName" value="MyCompany_API" />
</bean>
<bean id="mycompanyAuthorisationExceptionHandler" class="uk.co.mycompany.api.auth.MyCompanyAuthorisationExceptionHandler" />
<!-- This is not working as there is a problem with our contexts are set up. For now use the JDBC token store
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/>
-->
<bean id="tokenStore" class="uk.co.mycompany.api.auth.MyCompanyTokenStore">
<constructor-arg index="0"><ref bean="dataSourceSecurity"/></constructor-arg>
</bean>
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<!-- <property name="tokenEnhancer" ref="securityServiceTokenEnhancer" /> -->
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<oauth:resource-server id="resourceServerFilter" resource-id="MyCompany_API" token-services-ref="tokenServices" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:refresh-token />
<oauth:password/>
</oauth:authorization-server>
<bean id="mycompanyDaoAuthenticationProvider" class="uk.co.mycompany.security.auth.MyCompanyDaoAuthenticationProvider">
<constructor-arg ref="mycompanyUserDetailsService" />
</bean>
<security:authentication-manager>
<security:authentication-provider ref="mycompanyDaoAuthenticationProvider"/>
</security:authentication-manager>
=====
@Import({ OAuth2ServerConfig.class, ResourceServerConfig.class, AuthorizationServerConfig.class, WebSecurityConfig.class,ValidationConfig.class,SecurityLibraryContext.class, ServicesConfig.class })
@Configuration
@EnableWebMvc
public class MyCompanyApiConfig implements WebMvcConfigurer {
}
=====
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/** Logger for all logging. */
private static final Logger LOGGER = LoggerFactory.getLogger(WebSecurityConfig.class);
@PostConstruct
public void init() {
LOGGER.debug(String.format("Init %s", WebSecurityConfig.class.getName()));
}
@Autowired
private UserDetailsService myCompanyUserDetailsService;
@Bean
public OAuth2AuthenticationEntryPoint clientAuthenticationEntryPoint() {
OAuth2AuthenticationEntryPoint entrypoint = new OAuth2AuthenticationEntryPoint();
entrypoint.setRealmName("MyCompany_API/client");
entrypoint.setTypeName("Basic");
return entrypoint;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myCompanyDaoAuthenticationProvider());
auth.userDetailsService(myCompanyUserDetailsService).passwordEncoder(passwordEncoder());
}
// @Bean(name = "clientAuthenticationManager")
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public MyCompanyDaoAuthenticationProvider myCompanyDaoAuthenticationProvider() {
MyCompanyDaoAuthenticationProvider provider = new MyCompanyDaoAuthenticationProvider(myCompanyUserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
=====
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/** Logger for all logging. */
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceServerConfig.class);
private static final String RESOURCE_ID = "MyCompany_API";
@Autowired
private ComboPooledDataSource dataSourceSecurity;
@PostConstruct
public void init() {
LOGGER.debug(String.format("Init %s", ResourceServerConfig.class.getName()));
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
// @formatter:off
TokenStore tokenStore = new JdbcTokenStore(dataSourceSecurity);
resources.resourceId(RESOURCE_ID).tokenStore(tokenStore);
// @formatter:on
}
@Autowired
private UserDetailsService myCompanyUserDetailsService;
@Autowired
private MyCompanyAuthenticationExceptionHandler myCompanyAuthenticationExceptionHandler;
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers(HttpMethod.POST, "/oauth/token").permitAll()
.and().anonymous()
.and().httpBasic().authenticationEntryPoint(clientAuthenticationEntryPoint())
.and().exceptionHandling().accessDeniedHandler(oauthAccessDeniedHandler())
.and().csrf().disable();
// @formatter:off
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().httpBasic().authenticationEntryPoint(myCompanyAuthenticationExceptionHandler)
.and().authorizeRequests().accessDecisionManager(accessDecisionManager())
.and().anonymous().disable()
.addFilterBefore(preAuthFilter(), ChannelProcessingFilter.class)
// .addFilterBefore(resourceServerFilter(),
// PreAuthFilter.class)
.addFilterBefore(updateSavedRequestFilter(), FilterSecurityInterceptor.class)
.exceptionHandling().accessDeniedHandler(myCompanyAuthorisationExceptionHandler());
// @formatter:on
}
@Bean
public OAuth2AccessDeniedHandler oauthAccessDeniedHandler() {
return new OAuth2AccessDeniedHandler();
}
@Bean
public OAuth2AuthenticationEntryPoint clientAuthenticationEntryPoint() {
OAuth2AuthenticationEntryPoint entrypoint = new OAuth2AuthenticationEntryPoint();
entrypoint.setRealmName("MyCompany_API/client");
entrypoint.setTypeName("Basic");
return entrypoint;
}
@Bean
public MyCompanyDaoAuthenticationProvider myCompanyDaoAuthenticationProvider() {
MyCompanyDaoAuthenticationProvider provider = new MyCompanyDaoAuthenticationProvider(myCompanyUserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public UpdateSavedRequestFilter updateSavedRequestFilter() {
return new UpdateSavedRequestFilter();
}
@Bean
public PreAuthFilter preAuthFilter() {
return new PreAuthFilter();
}
@Bean
public MyCompanyAuthorisationExceptionHandler myCompanyAuthorisationExceptionHandler() {
return new MyCompanyAuthorisationExceptionHandler();
}
@Bean
public AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
decisionVoters.add(new WebExpressionVoter());
decisionVoters.add(new RoleVoter());
return new UnanimousBased(decisionVoters);
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
======
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/** Logger for all logging. */
private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationServerConfig.class);
@Autowired
// @Qualifier("clientAuthenticationManager")
@Qualifier(BeanIds.AUTHENTICATION_MANAGER)
private AuthenticationManager authenticationManager;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private UserDetailsService myCompanyUserDetailsService;
@Autowired
private ComboPooledDataSource dataSourceSecurity;
@PostConstruct
public void init() {
LOGGER.debug(String.format("Init %s", AuthorizationServerConfig.class.getName()));
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// @formatter:off
TokenStore tokenStore = new JdbcTokenStore(dataSourceSecurity);
endpoints.tokenStore(tokenStore).authenticationManager(authenticationManager).userDetailsService(myCompanyUserDetailsService);
// @formatter:on
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.withClientDetails(clientDetailsService);
// @formatter:on
}
}
=======
@Configuration
public class OAuth2ServerConfig {
@Value("${oauth.client.id}")
private String clientId;
@Value("${oauth.client.secret}")
private String clientSecret;
@Autowired
private ComboPooledDataSource dataSourceSecurity;
@Bean
public MyCompanyAuthenticationExceptionHandler myCompanyAuthenticationExceptionHandler() {
MyCompanyAuthenticationExceptionHandler handler = new MyCompanyAuthenticationExceptionHandler();
handler.setRealmName("MyCompany_API");
return handler;
}
@Bean
public DefaultTokenServices tokenServices() throws Exception {
DefaultTokenServices services = new DefaultTokenServices();
MyCompanyTokenStore store = new MyCompanyTokenStore(dataSourceSecurity);
services.setTokenStore(store);
services.setSupportRefreshToken(true);
services.setClientDetailsService(clientDetailsService());
return services;
}
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSourceSecurity);
}
}
=======
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AbstractAccessDecisionManager.checkAllowIfAllAbstainDecisions(AbstractAccessDecisionManager.java:70)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:89)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at uk.co.myCompany.api.controllers.MyAccountController$$EnhancerBySpringCGLIB$$9e8b984e.getUserAccountForDataFormat(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
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:334)
at uk.co.myCompany.api.auth.UpdateSavedRequestFilter.doFilterInternal(UpdateSavedRequestFilter.java:49)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
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:334)
at uk.co.myCompany.api.auth.PreAuthFilter.doFilterInternal(PreAuthFilter.java:64)
=======