Following the sample from Spring Boot: example code from GitHub everything seems to work fine.
But when I integrate Spring Boot Security OAuth2 in the project, my OAuth2 endpoints stop working. There's a warning in the logs:
2017-05-04 08:56:24.109 WARN 2827 --- [nio-8080-exec-1] o.glassfish.jersey.servlet.WebComponent : A servlet request to the URI http://127.0.0.1:8080/oauth/token contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
Which makes me think even though I'm not registering the endpoint, Jersey is capturing it and processing the body, making Spring MVC unable to accept the request...
My Jersey Config is:
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(InfoController.class);
}
}
And my info controller is very simple:
#Component
#Path("/me")
#Produces("application/json")
public class InfoController {
#GET
public String meAction() {
return "Hi";
}
}
And finally, the call I'm trying to make and it's causing the warning in the logs:
curl -X POST -u CLIENT_APPLICATION:123456789 http://127.0.0.1:8080/oauth/token -H "Accept: application/json" -d "password=aaa&username=aa&grant_type=password&client_id=CLIENT_APPLICATION"
Is there a known incompatibility between the two projects (spring-boot-starter-jersey and spring-security-oauth2 in that sense?
Removing the Jersey configuration makes it all work, but I need to use it on my controllers.
My configuration for OAuth2 is:
#Configuration
public class OAuth2ServerConfiguration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("OAuth2 Server");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/*").authenticated();
// #formatter:on
}
}
}
Then there's the security configuration itself:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final ApiUserDetailsService userDetailsService;
#Autowired
public WebSecurityConfiguration(ApiUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Thanks in advance!
It seems that Jersey is trying the handle the OAuth endpoint, which it shouldn't be. The reason is that the default mapping for Jersey is /*, which means that it will handle requests for all URLs. You can change that in a couple of ways:
Add an #ApplicationPath on top of your ResourceConfig subclass with a different mapping
#Component
#ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {}
You can add the mapping in your application.properties file
spring.jersey.application-path=/api
What this will do is prefix /api to all your Jersey endpoints, and also cause Jersey not to handle all request, only ones that begin with /api.
Related
I am having following configuration in Spring Security wiht OAuth2:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
//#EnableOAuth2Sso
public class FVSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("secret")
.roles("USER", "ROLE1")
.and()
.withUser("admin")
.password("password")
.roles("ADMIN","USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/**").hasRole("USER")
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler())
.and()
.formLogin();
}
Resource server configuraion:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("sampleClient")
.authorizedGrantTypes("implicit")
.scopes("read")
.autoApprove(true)
.accessTokenValiditySeconds(30)
.and()
.withClient("user")
.secret("secret")
.scopes("read", "write")
.autoApprove(true)
.authorizedGrantTypes(
"password","authorization_code", "refresh_token")
.and()
.withClient("admin")
.secret("password")
.scopes("read", "write", "custom")
.autoApprove(true)
.authorizedGrantTypes(
"password","authorization_code", "refresh_token");
}
And the following Rest Controller
#RestController
public class OAuthTestController {
#PreAuthorize("hasRole('ROLE_ROLE1') and #oauth2.hasScope('read')")
#RequestMapping(value="/api/user/test1", method = RequestMethod.GET)
public String testGETWithRole() {
return "[GET] Needs role ROLE1";
}
#RequestMapping(value="/api/admin/test1", method = RequestMethod.GET)
public String testWithAdminRole() {
return "[GET] Needs Admin role";
}
And the resource server:
#Configuration
#EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/api/**")
.and().authorizeRequests()
.antMatchers("/api/**").access("hasRole('USER')")
.antMatchers("/api/admin/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Basically I am expecting Spring to use OAuth2 for authentication and use spring security roles to allow resource access.
I generated and admin token and afterwards an user token. I am expecting when using admin token to be able to access /api/admin resource and when using user token to not be able.
I just noticed that the form login is not prompted always, so I suppose Spring remembers the authenticated user.
When I used the admin token I got access denied. When debugging in Spring sources I noticed that the authentication was an instance of UserNamePasswordAuthenticationPassword with the user details, not admin as expected. I suppose is because last time I used those credentials in login form.
Is it a way to force spring to use credentials for corresponding token, or to force Spring show login form each time?
P.S. I use Postman to test the API.
Thanks.
I am using spring security via spring boot.
I have two kinds of rest services.
public/** --> Every one can access and use these services
secure/** --> Only authenticated users can use.
#Slf4j
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(requestHeaderAuthenticationFilter(authenticationManager()),
BasicAuthenticationFilter.class)
.authorizeRequests().antMatchers("/secure/**").fullyAuthenticated();
}
#Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
final AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(true);
filter.setPrincipalRequestHeader("MY_HEADER");
filter.setInvalidateSessionOnPrincipalChange(true);
filter.setCheckForPrincipalChanges(false);
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}
When i want to access a resource under public i got exception.
exception: "org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException"
message: "MY_HEADER header not found in request."
Why does my filter activated under public resource while it is configured as ignored resource?
Thanks is advance
This is an issue in WebSecurity.ignoring() as discussed in Spring Security Github when using Beans as Filters.
You can work around this by removing the #Bean annotation in your Filter declaration.
// #Bean - Remove or Comment this
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
final AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setExceptionIfHeaderMissing(true);
filter.setPrincipalRequestHeader("MY_HEADER");
filter.setInvalidateSessionOnPrincipalChange(true);
filter.setCheckForPrincipalChanges(false);
filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return filter;
}
In the following setup, the TimingInterceptor and CORSHeaders interceptor execute on all URL requests, except for /resources/** URLs. How do I make the interceptors work for /resources/** URLs served by the ResourceHttpRequestHandler?
#EnableWebMvc //equivalent to mvc:annotation-driven
#Configuration
#PropertySource("classpath:configuration.properties")
public class WebConfig extends WebMvcConfigurerAdapter {
#Inject
private TimingInterceptor timingInterceptor;
#Inject
private CORSHeaders corsHeaders;
// equivalent to mvc:resources
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
// equivalent to mvc:interceptors
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timingInterceptor).addPathPatterns("/**");
registry.addInterceptor(corsHeaders).addPathPatterns("/**");
}
}
Update: As of Spring Framework 5.0.1 (and SPR-16034), interceptors are automatically mapped on ResourceHttpRequestHandler by default.
I think the configured interceptors aren't mappped on the resource handler, but on the one handling #RequestMapping requests.
Maybe try this instead?
#EnableWebMvc //equivalent to mvc:annotation-driven
#Configuration
#PropertySource("classpath:configuration.properties")
public class WebConfig extends WebMvcConfigurerAdapter {
#Inject
private TimingInterceptor timingInterceptor;
#Inject
private CORSHeaders corsHeaders;
// equivalent to mvc:resources
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public MappedInterceptor timingInterceptor() {
return new MappedInterceptor(new String[] { "/**" }, timingInterceptor);
}
#Bean
public MappedInterceptor corsHeaders() {
return new MappedInterceptor(new String[] { "/**" }, corsHeaders);
}
}
This should be better documented with SPR-10655.
I never tried to use Spring interceptors for serving resources. The power of interceptors is to have a hook before controller and between controller and view.
To add pre- or post-processing around resources, you'd better use filters.
I'm using Spring-Security 3.2.4 and Spring Boot 1.1.0 (and it's related dependencies versions 4.X).
I'm writing a web application that will be run in an embedded tomcat.
I'm trying to add two additional filters(not related to Spring security) that one of them will be invoked before the Spring-Security-FilterChainProxy and the other one will be invoked after the Spring-Security-FilterChainProxy.
My Spring-Security configuration files:
#Configuration
#EnableWebMvcSecurity
public class SecurityCtxConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("pass").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.usernameParameter("user").passwordParameter("password");
}
}
And the Main class (Application.class):
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
#Bean
RequestFilter beforeSpringSecurityFilter(){
return new RequestFilter();
}
#Bean
RequestFilter afterSpringSecurityFilter(){
return new RequestFilter();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And the Filter implementation:
public class RequestFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}
Is there a way to controll the invocation order when taking in account the FilterChainProxy (that is beeing created by the WebSecurityConfigurerAdapter ?
To be percise, the required order is:
request-filter-1
Spring-Security FilterChain
request-filter-2
Thanks
Agree with everything stated by Dave Syer ;) but wished to add a Java Config example of using the FilterRegistrationBean.
In my situation, I was finding that my custom security filter (using Spring Security) was being fired twice for every request. Adding the FilterRegistrationBean config fixed this.
#Bean(name = "myFilter")
public MyAuthenticationFilter myAuthenticationFilter(final MyAuthenticationEntryPoint entryPoint) {
final MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setEntryPoint(entryPoint);
return filter;
}
/**
* We do this to ensure our Filter is only loaded once into Application Context
*
*/
#Bean(name = "authenticationFilterRegistration")
public FilterRegistrationBean myAuthenticationFilterRegistration(final MyAuthenticationFilter filter) {
final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
(Regarding my specific issue of filter being registered twice in Application Context - Rather than using a FilterRegistrationBean, I also found re-implementing the MyAuthenticationFilter to inherit from OncePerRequestFilter instead of GenericFilterBean also worked. However, OncePerRequestFilter support is from Servlet 3.x upwards and since I was writing a public library, support from Servlet 2.x may be needed)
The FilterChainProxy use by Spring Security is not Ordered (if it was you could order all your filters). But you should be able to register it in a FilterRegistrationBean which is Ordered and register your other filters the same way. In the case of the security filter you can inject it by name into the registration bean. The others you can probably inject by calling a #Bean method.
At some point spring boot exposed the security filter as a property. This is now pretty easy to do.
In you application.yml:
spring:
security:
filter:
order: 20
And some filter you want to invoke after Spring Security does it's thing:
#Bean
public FilterRegistrationBean<Filter> afterAuthFilterRegistrationBean() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
//a filter that extends OncePerRequestFilter
AfterAuthFilter afterAuthFilter = new AfterAuthFilter();
registrationBean.setFilter(afterAuthFilter);
//this needs to be a number greater than than spring.security.filter.order
registrationBean.setOrder(30);
return registrationBean;
}
For a filter that is executed before Spring security, set the order to a number less than 20.
If you are using web.xml approaches, you can follow this:
https://stackoverflow.com/a/11929129/1542363
If you using Java config approaches, you can do this in WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(your-request-filter-1, ChannelProcessingFilter.class);
http.addFilterAfter(your-request-filter-2, SwitchUserFilter.class);
}
Always check the library version you are using, and refer to the specific document for the correct order of the filter chains:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#ns-custom-filters
Or, if you using AbstractSecurityWebApplicationInitializer, you can use the insertFilters or appendFilters.
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
More info You can refer this:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-multipart
I am still wresting with various annotations in setting up a test context under spring boot.
I have been referring to this article, which is refreshingly clear on how to deal with various contexts under Spring Boot. The problem remaining is that I cannot seem to find an annotation combination that will make the springSecurityFilterChain visible in both the main application context (driven from here):
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
and from the test application context begun here:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestPersistenceConfig.class,MvcConfig.class,SecurityConfig.class},loader=AnnotationConfigContextLoader.class)
//#SpringApplicationConfiguration(classes = {TestPersistenceConfig.class,MvcConfig.class,SecurityConfig.class})
#WebAppConfiguration
public class ApplicationIntegrationTest {
MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
//#Resource(name="springSecurityFilterChain")
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Autowired
private UserDao userDao;
#Autowired
private ClientDao clientDao;
#Autowired
private RoleDao roleDao;
UUID key = UUID.fromString("f3512d26-72f6-4290-9265-63ad69eccc13");
#Before
public void setup() {
// using the web application to initate the mock
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilter(springSecurityFilterChain).build();
// our other choice is using another controller config
//mockMvc = MockMvcBuilders.annotationConfigSetup(ExampleApplicationContext.class).build();
// here we should build up the data structure using hibernate
List<Client> clients = new ArrayList<>();
Client clientEN = new Client();
clientEN.setDeviceId("444444444");
clientEN.setLanguage("en-EN");
clientEN.setAgentId("444444444|68:5b:35:8a:7c:d0");
Client clientENDomain = clientDao.save(clientEN);
clients.add(clientENDomain);
List<Role> roles = new ArrayList<>();
Role roleUser = new Role();
roleUser.setRole("user");
Role roleUserDomain = roleDao.save(roleUser);
roles.add(roleUserDomain);
Role roleAdmin = new Role();
roleAdmin.setRole("admin");
Role roleAdminDomain = roleDao.save(roleAdmin);
roles.add(roleAdminDomain);
User user = new User();
user.setLogin("user");
user.setPassword("password");
user.setClients(clients);
user.setRoles(roles);
userDao.save(user);
}
#Test
public void thatViewBootstrapUsesHttpNotFound() throws Exception {
// testing that a correct login into the form will result in a cookie being set
MvcResult result = mockMvc.perform(post("/login")
.param("username", "user").param("password", "password")).andReturn();
Cookie c = result.getResponse().getCookie("my-cookie");
Cookie[] cookies = result.getResponse().getCookies();
for (int i = 0; i <= cookies.length; i++) {
System.out.println("cookie " + i + " name: " + cookies[i].getName());
System.out.println("cookie " + i + " value: " + cookies[i].getValue());
}
//assertThat(c.getValue().length(), greaterThan(10));
// No cookie; 401 Unauthorized
mockMvc.perform(get("/")).andExpect(status().isUnauthorized());
// With cookie; 200 OK
mockMvc.perform(get("/").cookie(c)).andExpect(status().isOk());
// Logout, and ensure we're told to wipe the cookie
result = mockMvc.perform(delete("/session")).andReturn();
c = result.getResponse().getCookie("my-cookie");
assertThat(c.getValue().length(), is(0));
}
}
By the way #SpringApplicationConfiguration doesn't seem to work in any circumstance, contrary to the doco. The security config is as follows:
#Configuration
#EnableWebMvcSecurity
#ComponentScan({
"com.touchcorp.touchpoint.security",
"com.touchcorp.touchpoint.service",
"com.touchcorp.touchpoint.model.dao"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DeviceUsernamePasswordAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Order(2)
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=1")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
}
Can anyone see why the springSecurityFilterChain is invisible ("No beans of FileterChainProxy type found"). Thanks, I'm pulling my hair out here.
I think I am just a bit unclear as to the purpose of all the annotations. The Spring Boot reference is good, but it doesn't really extend beyond an established baseline. It seems that as soon as you have to combine spring security, hibernate and mvc together, it starts to get complicated and its not clear as to what one is to do.
I would be worried about why #SpringApplicationConfiguration is not working because it is in extensive use elsewhere (e.g. in Spring Boot samples) and it works fine there. Maybe a classpath issue? How about linking to a complete project that others can try to reproduce your problem?
You have 2 different application contexts (one for the test and one in your Application) so it wouldn't be surprising if they behaved differently. In particular the Application has #EnableAutoConfiguration and you test (as far as we can see) does not, so there's one difference that's worth looking into. But nothing is obviously wrong with the test.
Here's an example of a test that autowires the Security filter: https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/sparklr/src/test/java/org/springframework/security/samples/config/ApplicationConfigurationTests.java. It works. Here's another: https://github.com/cloudfoundry/uaa/blob/master/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/audit/AuditCheckMvcMockTests.java.
Thanks to you Dave Syer,
I have made a couple of changes that seem to resolve the various missing pieces:
a test class that begins with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestApplicationConfig.class,TestPersistenceConfig.class,MvcConfig.class,SecurityConfig.class},loader=AnnotationConfigWebContextLoader.class)
#WebAppConfiguration
public class ApplicationIntegrationTest {
and a "marker" Config class, which acts as a component scanner:
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.touchcorp.touchpoint"})
public class TestApplicationConfig {
}
All pars seem to work aside from the data layer, which cannot find any of my domain objects, but this appears to be restricted to the JPA/Hibernate config, not so much an application problem.
Thanks again.