I made a simple rest service and I wanted to add a simple security on the api.
So I created an WebSecurityConfigurerAdapter :
package org.test.subscription.webservice.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("test1").password("test1").roles("superAdminRole").and()
.withUser("test2").password("test2").roles("superAdminRole");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest().hasRole("webserviceReadRole").and()
.csrf().disable();
}
}
And this is my main runner:
package org.test.subscription.webservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
But when I call something inside my API,I'm not authenticate at all, and I get my "hello world" response. So he never check my role. Any ideas why?
Your example should work as you expect it (tested in boot 1.5.7 and also 2.0.0 M3). So I would recommend that you try the following. First verify some trivial things:
That you do not have anything that disables security checks in your application.properties or the corresponding yaml (e.g. something like security.ignored and Co.).
That you do not have another WebSecurityConfigurerAdapter in your application.
That your classes are scanned correctly by Spring.
That you do not have some strange cookies in the browser. E.g. try the same request by starting the browser in private mode or use curl or something similar.
If it still does not work enable the Spring Security debugger for a deeper insight on what is going on under the hood and why you see unexpected behavior. This may be achieved in the following way:
#EnableWebSecurity(debug = true)
This will hint Spring to print lots of extra details which will help you find out what is wrong. In your setup you should see something like the following in the logs when you issue an request.
First, the request itself with the proper headers. Example with the most important parts:
Request received for GET '/path/to/your/api':
org.apache.catalina.connector.RequestFacade#58a4ad1c
authorization: Basic dGVzdDE6dGVzdDE=
cookie: JSESSIONID=9E4EBB889BB178E05446104EF2787C2F
Then you will see the filter chain managed by the FilterChainProxy and matched with your request (note that there might be other filter chains depending on the setup of your app - the log shows the matched chain which may not be the one that you expect):
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
Then lots of DEBUG messages will follow in the log. Please pay special attention to the messages created around the BasicAuthenticationFilter:
2017-10-07 14:42:21.644 DEBUG 56071 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /pat/to/your/api at position 6 of 12 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2017-10-07 14:42:21.645 DEBUG 56071 --- [nio-8080-exec-2] o.s.s.w.a.www.BasicAuthenticationFilter : Basic Authentication Authorization header found for user 'test1'
2017-10-07 14:42:21.645 DEBUG 56071 --- [nio-8080-exec-2] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2017-10-07 14:42:21.647 DEBUG 56071 --- [nio-8080-exec-2] o.s.s.w.a.www.BasicAuthenticationFilter : Authentication success: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#8fc16c08: Principal: org.springframework.security.core.userdetails.User#6924ddf: Username: test1; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_superAdminRole; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 9E4EBB889BB178E05446104EF2787C2F; Granted Authorities: ROLE_superAdminRole
2
And also to the FilterSecurityInterceptor which should output the successful authorization message:
2017-10-07 14:42:21.649 DEBUG 56071 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#8fc16c08: Principal: org.springframework.security.core.userdetails.User#6924ddf: Username: test1; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_superAdminRole; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 9E4EBB889BB178E05446104EF2787C2F; Granted Authorities: ROLE_superAdminRole
2017-10-07 14:42:21.649 DEBUG 56071 --- [nio-8080-exec-2] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#1ca8b2d, returned: 1
2017-10-07 14:42:21.649 DEBUG 56071 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
Armed with all these details approach SO further :-)
Related
I am working with Keycloak 16.1.0, spring boot 2.6.2 and an external application client that sends a bearer token in to my server application to the endpoint http://romanmed-host:8888/actuator/health.
By cranking the debugging level up to maximum, I can see the access token before its processed. I can verify that its accurate by using the JWT Debug site JSON Web Tokens to verify that the signature is correct.
Yet several lines later in the output log the same bearer token appears to be somewhat truncated, its listed with an error saying that it failed to verify. When checked by using the JWT site indicates a signature error, but the token content is correct.
Naturally I would like to know why it appears to be truncated and what I can do about it.
I can match the output from the client program to the server and its not been changed, so truncation must occur within the server program.
The program is accepting the request by a get request, since the token can be checked by JWT as valid at this point, its not truncated by the get request input method.
I have not inserted a filter in the security filter chain, so I can see how any of my code could be doing anything to invalid the token.
Other than the Failed to verify token no other error messages are generated, suggesting that until this point everything is correct.
I can see that the WebAsyncManagerIntegrationFilter, SecurityContextPersistenceFilter, HeaderWriterFilter,KeycloakPreAuthActionsFilter and KeycloakAuthenticationProcessingFilter have all been invoked.
I am assuming that the problem is somewhere within the KeycloakAuthenticationProcessingFilter, but I don't understand why the token appears to have been truncated at this point.
The received bearer token is
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3dUhKc1pvWnduelVsU1Zqc2JyTkxsbUNhR0ZIVkV0cTcyQkI5V0pORTVVIn0.eyJleHAiOjE2NDY0MDI3NTAsImlhdCI6MTY0NjQwMjQ1MCwianRpIjoiMjIyMjUxZDgtNDYxMy00OGQwLWEwNzAtMjU5YTYyY2NhZDkyIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg1L2F1dGgvcmVhbG1zL0Jvb3RBZG1pbiIsImF1ZCI6WyJybS1jb25maWctc2VydmVyIiwiYXBwLXRvZG8iLCJhY2NvdW50Il0sInN1YiI6Ijc4ZTU1YjhiLWQ5MjAtNGQ0Yi1hNWQ5LWIyZDk3MDYzNDgyYiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwcC1hZG1pbiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDozMDAwMSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtYm9vdGFkbWluIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicm0tY29uZmlnLXNlcnZlciI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYXBwLWFkbWluIjp7InJvbGVzIjpbImFjdHVhdG9yIl19LCJhcHAtdG9kbyI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJhcHAtYWRtaW4iLCJjbGllbnRIb3N0IjoiMTI3LjAuMC4xIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtYXBwLWFkbWluIiwiY2xpZW50QWRkcmVzcyI6IjEyNy4wLjAuMSJ9.fwQPLiSIrUSjnRnTBrd1vvGic49OSf7aGDemc0TdmTshZzJ-eYhiEqnAh9-QU2rxDayPIhoIzA9CgBXmGPCnl1Qu4CujDddpBcLpnjszBoBdzwjDgpShgwFpGk0fGCM0fxtSZgMWRfeS_sRjBpRzZ42GelCYZ2E1kZX_E7o_LB3thpiv5oYqgTNucusNmzpm0-iFcEUe5rfnu2ZOHI_hLQvIYKlGURnNld4jov-KDLf2QTh2h3XqjbsGHG9PDq4MbFPhKY_9yF0jQkhF6F3oYrw9MIH4SbemrR-CHw6-aWqGmgucjJ7iKMY5o86HxLPu2tzM06NdaurQZX4ImLCBlQ
Its truncated format is
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3dUhKc1pvWnduelVsU1Zqc2JyTkxsbUNhR0ZIVkV0cTcyQkI5V0pORTVVIn0.eyJleHAiOjE2NDY0MDI3NTAsImlhdCI6MTY0NjQwMjQ1MCwianRpIjoiMjIyMjUxZDgtNDYxMy00OGQwLWEwNzAtMjU5YTYyY2NhZDkyIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg1L2F1dGgvcmVhbG1zL0Jvb3RBZG1pbiIsImF1ZCI6WyJybS1jb25maWctc2VydmVyIiwiYXBwLXRvZG8iLCJhY2NvdW50Il0sInN1YiI6Ijc4ZTU1YjhiLWQ5MjAtNGQ0Yi1hNWQ5LWIyZDk3MDYzNDgyYiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwcC1hZG1pbiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDozMDAwMSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtYm9vdGFkbWluIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicm0tY29uZmlnLXNlcnZlciI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYXBwLWFkbWluIjp7InJvbGVzIjpbImFjdHVhdG9yIl19LCJhcHAtdG9kbyI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJhcHAtYWRtaW4iLCJjbGllbnRIb3N0IjoiMTI3LjAuMC4xIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtYXBwLWFkbWluIiwiY2xpZW50QWRkcmVzcyI6IjEyNy4wLjAuMSJ9
The debug log is
servletPath:/actuator/health
pathInfo:null
headers:
accept-encoding: gzip
user-agent: ReactorNetty/1.0.13
host: romanmed-host:8888
authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3dUhKc1pvWnduelVsU1Zqc2JyTkxsbUNhR0ZIVkV0cTcyQkI5V0pORTVVIn0.eyJleHAiOjE2NDY0MDI3NTAsImlhdCI6MTY0NjQwMjQ1MCwianRpIjoiMjIyMjUxZDgtNDYxMy00OGQwLWEwNzAtMjU5YTYyY2NhZDkyIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg1L2F1dGgvcmVhbG1zL0Jvb3RBZG1pbiIsImF1ZCI6WyJybS1jb25maWctc2VydmVyIiwiYXBwLXRvZG8iLCJhY2NvdW50Il0sInN1YiI6Ijc4ZTU1YjhiLWQ5MjAtNGQ0Yi1hNWQ5LWIyZDk3MDYzNDgyYiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwcC1hZG1pbiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDozMDAwMSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtYm9vdGFkbWluIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicm0tY29uZmlnLXNlcnZlciI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYXBwLWFkbWluIjp7InJvbGVzIjpbImFjdHVhdG9yIl19LCJhcHAtdG9kbyI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJhcHAtYWRtaW4iLCJjbGllbnRIb3N0IjoiMTI3LjAuMC4xIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtYXBwLWFkbWluIiwiY2xpZW50QWRkcmVzcyI6IjEyNy4wLjAuMSJ9.fwQPLiSIrUSjnRnTBrd1vvGic49OSf7aGDemc0TdmTshZzJ-eYhiEqnAh9-QU2rxDayPIhoIzA9CgBXmGPCnl1Qu4CujDddpBcLpnjszBoBdzwjDgpShgwFpGk0fGCM0fxtSZgMWRfeS_sRjBpRzZ42GelCYZ2E1kZX_E7o_LB3thpiv5oYqgTNucusNmzpm0-iFcEUe5rfnu2ZOHI_hLQvIYKlGURnNld4jov-KDLf2QTh2h3XqjbsGHG9PDq4MbFPhKY_9yF0jQkhF6F3oYrw9MIH4SbemrR-CHw6-aWqGmgucjJ7iKMY5o86HxLPu2tzM06NdaurQZX4ImLCBlQ
accept: application/vnd.spring-boot.actuator.v2+json, application/vnd.spring-
boot.actuator.v1+json, application/json
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
KeycloakPreAuthActionsFilter
KeycloakAuthenticationProcessingFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
KeycloakSecurityContextRequestFilter
KeycloakAuthenticatedActionsFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2022-03-04 14:03:30.088 TRACE 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#2caa9666, org.springframework.security.web.context.SecurityContextPersistenceFilter#67683210, org.springframework.security.web.header.HeaderWriterFilter#58a9e64d, org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter#3fecb076, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter#41d84abb, org.springframework.security.web.authentication.logout.LogoutFilter#3e563293, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#25511895, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#21202507, org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter#62159fd, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter#28e8dee7, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#263f6e96, org.springframework.security.web.session.SessionManagementFilter#d3b0397, org.springframework.security.web.access.ExceptionTranslationFilter#75d0cac6, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#2267b0bb]] (1/1)
2022-03-04 14:03:30.088 DEBUG 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Securing GET /actuator/health
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/14)
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/14)
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Created SecurityContextImpl [Null authentication]
2022-03-04 14:03:30.089 DEBUG 99667 --- [.1-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/14)
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking KeycloakPreAuthActionsFilter (4/14)
2022-03-04 14:03:30.089 DEBUG 99667 --- [.1-8888-exec-10] o.k.adapters.PreAuthActionsHandler : adminRequest http://romanmed-host:8888/actuator/health
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking KeycloakAuthenticationProcessingFilter (5/14)
2022-03-04 14:03:30.089 DEBUG 99667 --- [.1-8888-exec-10] f.KeycloakAuthenticationProcessingFilter : Attempting Keycloak authentication
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.k.adapters.RequestAuthenticator : --> authenticate()
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.k.adapters.RequestAuthenticator : try bearer
2022-03-04 14:03:30.089 DEBUG 99667 --- [.1-8888-exec-10] o.k.a.BearerTokenRequestAuthenticator : Found [1] values in authorization header, selecting the first value for Bearer.
2022-03-04 14:03:30.089 DEBUG 99667 --- [.1-8888-exec-10] o.k.a.BearerTokenRequestAuthenticator : Verifying access_token
2022-03-04 14:03:30.089 TRACE 99667 --- [.1-8888-exec-10] o.k.a.BearerTokenRequestAuthenticator : access_token: eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3dUhKc1pvWnduelVsU1Zqc2JyTkxsbUNhR0ZIVkV0cTcyQkI5V0pORTVVIn0.eyJleHAiOjE2NDY0MDI3NTAsImlhdCI6MTY0NjQwMjQ1MCwianRpIjoiMjIyMjUxZDgtNDYxMy00OGQwLWEwNzAtMjU5YTYyY2NhZDkyIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg1L2F1dGgvcmVhbG1zL0Jvb3RBZG1pbiIsImF1ZCI6WyJybS1jb25maWctc2VydmVyIiwiYXBwLXRvZG8iLCJhY2NvdW50Il0sInN1YiI6Ijc4ZTU1YjhiLWQ5MjAtNGQ0Yi1hNWQ5LWIyZDk3MDYzNDgyYiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwcC1hZG1pbiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cDovL2xvY2FsaG9zdDozMDAwMSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtYm9vdGFkbWluIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicm0tY29uZmlnLXNlcnZlciI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYXBwLWFkbWluIjp7InJvbGVzIjpbImFjdHVhdG9yIl19LCJhcHAtdG9kbyI6eyJyb2xlcyI6WyJhY3R1YXRvciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJhcHAtYWRtaW4iLCJjbGllbnRIb3N0IjoiMTI3LjAuMC4xIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtYXBwLWFkbWluIiwiY2xpZW50QWRkcmVzcyI6IjEyNy4wLjAuMSJ9.signature
2022-03-04 14:03:30.091 DEBUG 99667 --- [.1-8888-exec-10] o.k.a.BearerTokenRequestAuthenticator : Failed to verify token
2022-03-04 14:03:30.091 DEBUG 99667 --- [.1-8888-exec-10] o.k.adapters.RequestAuthenticator : Bearer FAILED
2022-03-04 14:03:30.091 DEBUG 99667 --- [.1-8888-exec-10] f.KeycloakAuthenticationProcessingFilter : Auth outcome: FAILED
2022-03-04 14:03:30.092 TRACE 99667 --- [.1-8888-exec-10] f.KeycloakAuthenticationProcessingFilter : Failed to process authentication request
org.keycloak.adapters.springsecurity.KeycloakAuthenticationException: Invalid authorization header, see WWW-Authenticate header for detailsr code here
The code is taken from an example by Thomas Darimont at Securing Spring Boot Admin & actuator endpoints with Keycloak and assumed to be correct.
The code is as follows
import lombok.extern.slf4j.Slf4j;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.springboot.KeycloakSpringBootProperties;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import java.security.Principal;
#KeycloakConfiguration
#Slf4j
#EnableConfigurationProperties(KeycloakSpringBootProperties.class)
class KeycloakSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.csrf().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.to(
InfoEndpoint.class,
HealthEndpoint.class
)).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint())
.hasRole("ACTUATOR")
.anyRequest().permitAll()
;
}
/**
* Use {#link KeycloakAuthenticationProvider}
*
* #param auth
* #throws Exception
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix("ROLE_");
grantedAuthorityMapper.setConvertToUpperCase(true);
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(buildSessionRegistry());
}
#Bean
protected SessionRegistry buildSessionRegistry() {
return new SessionRegistryImpl();
}
/**
* Allows to inject requests scoped wrapper for {#link KeycloakSecurityContext}.
*
* Returns the {#link KeycloakSecurityContext} from the Spring
* {#link ServletRequestAttributes}'s {#link Principal}.
* <p>
* The principal must support retrieval of the KeycloakSecurityContext, so at
* this point, only {#link KeycloakPrincipal} values and
* {#link KeycloakAuthenticationToken} are supported.
*
* #return the current <code>KeycloakSecurityContext</code>
*/
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakSecurityContext provideKeycloakSecurityContext() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Principal principal = attributes.getRequest().getUserPrincipal();
if (principal == null) {
return null;
}
if (principal instanceof KeycloakAuthenticationToken) {
principal = Principal.class.cast(KeycloakAuthenticationToken.class.cast(principal).getPrincipal());
}
if (principal instanceof KeycloakPrincipal) {
return KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext();
}
return null;
}
The problem is the set up of the client and server programs.
The server used the value
auth-server-url: http://localhost:8085/auth
in its application.yml file to define the location of the Keycloak server, the client used the value
auth-server-url: http://romanmed-host:8085/auth
to define the location of Keycloak where the machine name romanmed-host is an alias for localhost. Having changed these values to be the same value, everything works as expected.
The diagnostics generated by the debugging/trace code are confusing, string described as truncated header seems to be truncated the bearer token, with out the signature. What the 'Keycloak` diagnostic is attempting to print is the part of the token which defines the tokens permissions and not the signature section.
Running the entire bearer token through the JWT site does show that the token is valid, because its a correctly encoded token and is legitimate.
The problem is not the token, but the way the token is being used! The client was expecting a legal signed token generated by them instance of Keycloak that it knew about, what it got was a legal signed token generated by Keycloak with a different address, which it correctly objected to.
The problem being the nature of the generated error message, it just claimed that the token signature was invalid, had it said something about an invalid/unexpected hostname, the nature of the problem would have been rather more obvious and resolved much faster. Keycloak is design to be flexible, so error messages tend to more vague to cover all situations, hence the message there is something wrong with your bearer token signature which is correct, but vague.
There seems to several schools of thought on how to resolve issues like this, one is to use an raw ip address which will always resolve to the same value. Thus avoid problems like this. This suffers from if the Keycloak server is moved to another machine there are lots of values to change.
My solution is to define an alias value in the hosts/dns server for the address of the eycloak server and always use that value in the support files. Hence if the Keycloak server is ever moved to another address, there is only one value to change.
I try to setup an OAuth2RestTemplate in a spring boot project. I tried to follow a custom setup, but for some reason I always get authorization_request_not_found error when trying to access a secured resource.
My AppConfig:
#EnableOAuth2Client
#SpringBootApplication
public class AppConfig {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, args);
}
#Bean
RestTemplate restTemplate(OAuth2ProtectedResourceDetails oauth2RemoteResource) {
return new OAuth2RestTemplate(oauth2RemoteResource);
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/", "/login/**").permitAll()
.mvcMatchers("/secure").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login()
.and()
.oauth2Client();
}
}
Controller:
#GetMapping("/secure")
public String secure() {
Object result = restTemplate.getForObject("https://esi.evetech.net/latest/alliances", Object.class);
return "/";
}
application.yml:
security:
oauth2:
client:
id: eve
client-id: clientId
client-secret: secret
grant-type: authorization_code
user-authorization-uri: https://login.eveonline.com/v2/oauth/authorize
access-token-uri: https://login.eveonline.com/v2/oauth/token
pre-established-redirect-uri: http://localhost:8080/login/oauth2/code/eve
use-current-uri: false
scope:
- esi-characters.read_blueprints.v1
resource:
user-info-uri: https://login.eveonline.com/oauth/verify
spring:
security:
oauth2:
client:
registration:
eve:
authorization-grant-type: authorization_code
client-id: clientId
client-secret: secret
redirect-uri: http://localhost:8080/login/oauth2/code/eve
client-authentication-method: basic
scope:
- esi-characters.read_blueprints.v1
provider:
eve:
authorization-uri: https://login.eveonline.com/v2/oauth/authorize
token-uri: https://login.eveonline.com/v2/oauth/token
user-info-uri: https://login.eveonline.com/oauth/verify
user-name-attribute: CharacterID
Now I find this also interesting since I am not sure why I have to declare these properties twice.
I mean the OAuth2RestTemplate takes an AuthorizationCodeResourceDetails instance in the constructor in AppConfig which reads the security.oatuh2.client properties, while the DefaultOAuth2ClientContext inside the OAuth2RestTemplate constructor reads the other, I just don't understand why is it like that? Is there a simple setup?
Anyway going back to the original problem, when I access /secure it redirects me to the login page, where I login successfully which then redirects me back to the /secure page, so far so good.
Just before calling the restTemplate, I have this in the log:
2020-05-06 22:55:31.528 DEBUG 29177 --- [nio-8080-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /secure; Attributes: [authenticated]
2020-05-06 22:55:31.528 DEBUG 29177 --- [nio-8080-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken#3bb07c9: Principal: Name: [<ID>], Granted Authorities: [[ROLE_USER, SCOPE_esi-characters.read_blueprints.v1]], User Attributes: [{CharacterID=<ID>, CharacterName=<Name>, ExpiresOn=2020-05-06T22:15:31, Scopes=esi-characters.read_blueprints.v1, TokenType=Character, CharacterOwnerHash=<Hash>, IntellectualProperty=EVE}]; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 472A39D41FE34AC90781A80E39E4A52D; Granted Authorities: ROLE_USER, SCOPE_esi-characters.read_blueprints.v1
2020-05-06 22:55:31.529 DEBUG 29177 --- [nio-8080-exec-4] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#47a65378, returned: 1
But after the restTemplate is called:
2020-05-06 23:00:31.482 DEBUG 29177 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Failed to complete request: org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval
2020-05-06 23:00:31.483 DEBUG 29177 --- [nio-8080-exec-4] 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#205366de
2020-05-06 23:00:31.483 DEBUG 29177 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-05-06 23:00:31.484 DEBUG 29177 --- [nio-8080-exec-4] o.s.s.web.DefaultRedirectStrategy : Redirecting to 'https://login.eveonline.com/v2/oauth/authorize?client_id=<clientId>&redirect_uri=http://localhost:8080/login/oauth2/code/eve&response_type=code&scope=esi-characters.read_blueprints.v1&state=<state>'
As you can see it takes me back to the login page, so I login back again and this time I get the error: [authorization_request_not_found]
log:
2020-05-06 23:02:27.285 DEBUG 29177 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy : /login/oauth2/code/eve?code=<code>&state=<state> at position 8 of 17 in additional filter chain; firing Filter: 'OAuth2LoginAuthenticationFilter'
2020-05-06 23:02:27.285 DEBUG 29177 --- [nio-8080-exec-6] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login/oauth2/code/eve'; against '/login/oauth2/code/*'
2020-05-06 23:02:27.285 DEBUG 29177 --- [nio-8080-exec-6] .s.o.c.w.OAuth2LoginAuthenticationFilter : Request is to process authentication
2020-05-06 23:02:27.287 DEBUG 29177 --- [nio-8080-exec-6] .s.o.c.w.OAuth2LoginAuthenticationFilter : Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [authorization_request_not_found]
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [authorization_request_not_found]
at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:163) ~[spring-security-oauth2-client-5.2.2.RELEASE.jar:5.2.2.RELEASE]
I am lost at this point, not sure why I am getting this.
I also uploaded the project to github in case it's easier to check out what's wrong with it.
Thanks
We have a CustomAuthenticationProvider(AuthenticationProvider) developed for Spring which works with CustomAuthenticationRequest(Authentication), CustomAuthentication(Authentication), a CustomUser.
Once we validate credentials when our Controller is invoked we create a CustomAuthenticationRequest based on the credentials.
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(new CustomAuthenticationRequest(new CustomUser(account.getUsername())));
Debug login confirms that the CustomAuthenticationRequest has been stored in the HTTPSession.
HttpSessionSecurityContextRepository - SecurityContext 'org.springframework.security.core.context.SecurityContextImpl#730db7d8: Authentication: pro.someplace.spring.CustomAuthenticationRequest#730db7d8' stored to HttpSession: 'org.apache.catalina.session.StandardSessionFacade#5da80010
The WebSecurityConfigurerAdapter registers our AuthenticationProvider:
#Override
public void configure(AuthenticationManagerBuilder builder)
throws Exception {
builder.authenticationProvider(new CustomAuthenticationProvider());
}
And establishes what can and cannot be seen by anonymous and authenticated users.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/registration").permitAll()
.anyRequest()
.authenticated();
}
The problem we have is that before the FilterSecurityInterceptor can consult which AuthenticationProvider is appropriate the AnonymousAuthenticationFilter steps in:
o.s.s.w.a.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#4cc1f847: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffd148a: RemoteIpAddress: 127.0.0.1; SessionId: 74DB809F1CB5CFB1F977EC20B37B218E; Granted Authorities: ROLE_ANONYMOUS'
If I remove the AnonymousAuthenticationFilter then I cannot access permitAll() in the configuration (different error).
Curiously, I notice this logging message at the end of request processing:
SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
Ok. So the SecurityContextPersistenceFilter should have persisted the context in the HttpSessionSecurityContextRepository.
But when the next request appears the SecurityContextPersistenceFilter has no such object. Was it saved at all? Was it removed?
o.s.security.web.FilterChainProxy - /ordervalidator at position 2 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
o.s.s.w.c.HttpSessionSecurityContextRepository - No HttpSession currently exists
o.s.s.w.c.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
How can I configure spring to allow authenticated users where I want them and use my CustomAuthenticationProvider when available in the HTTPSession? Where is the security object and why is it not being stored?
And so will be as dumb as it sounds. The front end is React and so we were returning a cookie to the front end however the front end was not being returned to us. As soon as I manually insert cookies into requests I have trapped in either (Postman or Burp) my Context can be found.
This is a cautionary tale as there is a generational difference between the frontend React and the backend (Spring) developers. The backend expect cookies and this is assumed, the front end "...have no idea why we would want them and its a bad idea".
I am trying to use spring boot oauth2 to accomplish stateless authentication and authorisation. However, I am struggling to it working.
Here is my code:
#EnableAutoConfiguration
#ComponentScan
//#EnableEurekaClient
//#EnableZuulProxy
#Configuration
public class AuthServiceApp {
public static void main(String[] args) {
SpringApplication.run(AuthServiceApp.class, args);
}
}
Authorisation Config:
#Configuration
#EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager auth;
#Autowired
private DataSource dataSource;
#Autowired
private CustomUserDetailsService userDetailService;
#Autowired
private ClientDetailsService clientDetailsService;
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// #OFF
endpoints
.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(auth)
.userDetailsService(userDetailService)
.tokenStore(tokenStore());
// #ON
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #OFF
clients.jdbc(dataSource)
.withClient("client")
.secret("secret")
.authorizedGrantTypes("password","refresh_token", "client_credentials")
.authorities("USER")
.scopes("read", "write")
.autoApprove(true)
.accessTokenValiditySeconds(60)
.refreshTokenValiditySeconds(300);
// #ON
}
}
Resource Server Config:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
// #OFF
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
// .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/identity/**").authenticated();
// #ON
}
}
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #OFF
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
// #ON
}
}
Controller:
#RestController
#RequestMapping("/")
public class AuthController {
#PreAuthorize("#oauth2.hasScope('read')")
#GetMapping("/user")
public Principal getUser(Principal user) {
return user;
}
}
I can get the access token using POSTMAN. I am using the same access token in the header to get the user details as http://localhost:8082/identity/user before it gets expired. However, I get login page html response with following log on console:
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 301C6EDD36372CF9C553FCFCD4AA47E3; Granted Authorities: ROLE_ANONYMOUS'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.security.web.FilterChainProxy : /user at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.security.web.FilterChainProxy : /user at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.security.web.FilterChainProxy : /user at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/user'; against '/login'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /user; Attributes: [authenticated]
2017-05-24 22:55:16.071 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 301C6EDD36372CF9C553FCFCD4AA47E3; Granted Authorities: ROLE_ANONYMOUS
2017-05-24 22:55:16.071 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#55b4f25d, returned: -1
2017-05-24 22:55:16.071 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-4.2.2.RELEASE.jar:4.2.2.RELEASE]
But it seems like I have been authenticated when making first call to get the access token to oauth/token:
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /oauth/token; Attributes: [fullyAuthenticated]
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#50c8f5e8: Principal: org.springframework.security.core.userdetails.User#af12f3cb: Username: client; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#21a2c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 2F070B741A55BD1E47933621D9127780; Granted Authorities: USER
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#61f8721f, returned: 1
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.security.web.FilterChainProxy : /oauth/token reached end of additional filter chain; proceeding with original chain
2017-05-24 22:54:35.967 DEBUG 16899 --- [nio-8082-exec-6] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /oauth/token
2017-05-24 22:54:35.968 DEBUG 16899 --- [nio-8082-exec-6] .s.o.p.e.FrameworkEndpointHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
2017-05-24 22:54:35.975 DEBUG 16899 --- [nio-8082-exec-6] .o.p.p.ResourceOwnerPasswordTokenGranter : Getting access token for: client
2017-05-24 22:54:35.975 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
Hibernate: select user0_.id as id1_1_, user0_.enabled as enabled2_1_, user0_.name as name3_1_, user0_.password as password4_1_, user0_.username as username5_1_ from user user0_ where user0_.username=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.role_id as role_id2_2_0_, role1_.id as id1_0_1_, role1_.role as role2_0_1_ from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
2017-05-24 22:54:36.125 INFO 16899 --- [nio-8082-exec-6] o.s.s.o.p.token.store.JdbcTokenStore : Failed to find access token for token 180c2528-b712-4088-9cce-71e9cc7ccb94
2017-05-24 22:54:36.232 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2017-05-24 22:54:36.232 DEBUG 16899 --- [nio-8082-exec-6] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
May be I am configuring something wrong. What am I missing here?
I had a similar issue and found that the OAuth2AuthenticationProcessingFilter was not getting invoked by the filter chain and because of which, user was not getting authenticated and hence treated as anonymous.
Am using Spring-boot 1.5.3 version and I added below line in application.yml to fix the ordering.
security.oauth2.resource.filter-order=3
There must be a log statement present which shows that its getting invoked
DEBUG 34386 --- [nio-8082-exec-1] o.s.security.web.FilterChainProxy : /foo at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
Reference - https://github.com/spring-projects/spring-security-oauth/issues/993
To use
.authorizedGrantTypes("password","refresh_token", "client_credentials")
you need to open access to
/auth/token
in security configuration of your Authorization Server
#Override
protected void configure(HttpSecurity http) throws Exception {
code...
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.anyRequest().authenticated()
code...
}
I was facing the same problem and managed to fix it by explicitly disabling some spring autoconfiguration:
#EnableAutoConfiguration(exclude = [OAuth2AutoConfiguration::class,
SecurityAutoConfiguration::class, SecurityFilterAutoConfiguration::class])
Also check if you are using multiple OAuth2 filters coming from multiple auth-related dependices/libraries.
In my case I was using
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
Along with another custom OAuth2 library used in my organization, which caused the spring library to attempt to authenticate before the custom (correct) one, and with no credentials fed to it of course throwing this exact exception.
My service calls worked once I removed the Spring OAuth2 dependency.
so I have an existing SpringBoot project.
a) I added this at the top of my main #Configuration class
#Import({ WebSecurityConfiguration.class, OAuth2ServerConfiguration.class })
b) The contents of the above 2 classes are the same as what is in this project: https://github.com/royclarkson/spring-rest-service-oauth/tree/master/src/main/java/hello
c) I startup my app and see this as expected in the logs:
2015-10-28 20:05:40,037 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/authorize]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(java.util.Map<java.lang.String, java.lang.Object>,java.util.Map<java.lang.String, java.lang.String>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)
2015-10-28 20:05:40,037 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/authorize],methods=[POST],params=[user_oauth_approval]}" onto public org.springframework.web.servlet.View org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.approveOrDeny(java.util.Map<java.lang.String, java.lang.String>,java.util.Map<java.lang.String, ?>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)
2015-10-28 20:05:40,040 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/token],methods=[GET]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.getAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
2015-10-28 20:05:40,040 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/token],methods=[POST]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
2015-10-28 20:05:40,041 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/check_token]}" onto public java.util.Map<java.lang.String, ?> org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint.checkToken(java.lang.String)
2015-10-28 20:05:40,041 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/confirm_access]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint.getAccessConfirmation(java.util.Map<java.lang.String, java.lang.Object>,javax.servlet.http.HttpServletRequest) throws java.lang.Exception
2015-10-28 20:05:40,042 INFO [FrameworkEndpointHandlerMapping] Mapped "{[/oauth/error]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint.handleError(javax.servlet.http.HttpServletRequest)
d) When I go to /greeting I get the following as expected:
{ "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }
e) When I attempt to POST to /oauth/token I see the below in the logs... but I get a 404 and no token back. This has been driving me nuts now for 2 days trying to figure this out. Any help appreciated.
2015-10-28 20:08:59,713 DEBUG [FilterSecurityInterceptor] Secure object: FilterInvocation: URL: /oauth/token; Attributes: [fullyAuthenticated]
2015-10-28 20:08:59,713 DEBUG [FilterSecurityInterceptor] Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#7159411d: Principal: org.springframework.security.core.userdetails.User#8e81ee76: Username: clientapp; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: USER
2015-10-28 20:08:59,713 DEBUG [AffirmativeBased] Voter: org.springframework.security.web.access.expression.WebExpressionVoter#44aef1f8, returned: 1
2015-10-28 20:08:59,713 DEBUG [FilterSecurityInterceptor] Authorization successful
2015-10-28 20:08:59,713 DEBUG [FilterSecurityInterceptor] RunAsManager did not change Authentication object
2015-10-28 20:08:59,713 DEBUG [FilterChainProxy] /oauth/token reached end of additional filter chain; proceeding with original chain
2015-10-28 20:08:59,714 DEBUG [ExceptionTranslationFilter] Chain processed normally
2015-10-28 20:08:59,714 DEBUG [SecurityContextPersistenceFilter] SecurityContextHolder now cleared, as request processing completed
Again, I just get a 404
The problem is you are not adding Authorization in the header.Without adding header you cannot access the resources, and sometimes it shows error of Bad Credentials.For postman POST call for /oauth/token at there authorization is needed in header.
If i am not adding any authorization to the header.It shows me following error:-
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
You can create Authorization with the help of Base64Encoder
The problem was that in my main #Configuration annotated class I had a dispatcherServlet #Bean registered, which was intercepting all the requests. Once I changed the method name to something other than dispatcherServlet() it fixed the issue:
BAD:
#Bean
public ServletRegistrationBean dispatcherServlet() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean(cxfServlet, "/x/*");
}
GOOD:
#Bean
public ServletRegistrationBean thisFixedMyIssue() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean(cxfServlet, "/x/*");
}