Spring Boot 2.0.3 Oauth2 Security: Getting 401 error even when using access token in header
Try to added
@Order(SecurityProperties.BASIC_AUTH_ORDER)
for the securityConfig? so the chain will check your Resource server's config first.
And not sure if that your type error, remove the abstract from the resource server.
Aditya Kumar
Updated on June 13, 2022Comments
-
Aditya Kumar almost 2 years
I'm creating a spring boot 2.0 application and trying to enable oauth2 security. I have Auth server and Resource server in the same application as of now. My client and user details as well as token generated are persisted in databases (mysql) and database schema is the same as provided by spring documentation. When I hit the '/oauth/token/' endpoint providing clientId and clientSecret in header and user's credentials in body using Postman, I'm getting access token successfully.
{ "access_token": "bef2d974-2e7d-4bf0-849d-d80e8021dc50", "token_type": "bearer", "refresh_token": "32ed6252-e7ee-442c-b6f9-d83b0511fcff", "expires_in": 6345, "scope": "read write trust" }
But when I try to hit my rest api using this access token, I'm getting 401 Unauthorized error:
{ "timestamp": "2018-08-13T11:17:19.813+0000", "status": 401, "error": "Unauthorized", "message": "Unauthorized", "path": "/myapp/api/unsecure" }
The rest APIs I'm hitting are as follows:
http://localhost:8080/myapp/api/unsecure
http://localhost:8080/myapp/api/secure
myapp is the context path of my application.
For 'secure' api, I have provided access token in request header as described in Spring documentation:
Authorization: Bearer bef2d974-2e7d-4bf0-849d-d80e8021dc50
Whereas for unsecure api, I have tried with and without Authentication header. In all cases I'm getting same error for both apis.
Also when I try to print currently authenticated user, its getting printed as anonymousUser.
What I want are as follows:
1) I want my secure api to be accessible only when access token is provided in request header.
2) I want my unsecure api to be accessible by unauthorised user.
3) I should get currently authenticated user using SecurityContextHolder when accessing secure url.
My WebSecurityConfigurerAdapter is as follows:
@Configuration @EnableWebSecurity(debug=true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Autowired UserDetailsService userDetailsService; @Autowired private ClientDetailsService clientDetailsService; @Bean public PasswordEncoder userPasswordEncoder() { return new BCryptPasswordEncoder(8); } @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); } @Autowired public void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDetailsService) .passwordEncoder(userPasswordEncoder()); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean @Autowired public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){ TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler(); handler.setTokenStore(tokenStore); handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService)); handler.setClientDetailsService(clientDetailsService); return handler; } @Bean @Autowired public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception { TokenApprovalStore store = new TokenApprovalStore(); store.setTokenStore(tokenStore); return store; } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .cors().disable() .anonymous().disable() .authorizeRequests() .antMatchers("/index.html", "/**.js", "/**.css", "/").permitAll() .anyRequest().authenticated() .and() .httpBasic(); }
Here using antMatchers I have permitted static pages of Angular 6 application, as I'm planning to use those in my real app. And no, the following line does not work to allow static pages of angular application:
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
My AuthorizationServerConfigurerAdapter is as follows:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private DataSource dataSource; @Autowired UserDetailsService userDetailsService; @Autowired PasswordEncoder passwordEncoder; @Autowired TokenStore tokenStore; @Autowired private UserApprovalHandler userApprovalHandler; @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) { oauthServer .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .passwordEncoder(passwordEncoder); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore) .userApprovalHandler(userApprovalHandler) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } }
My ResourceServerConfigurerAdapter is as follows:
@Configuration @EnableResourceServer public abstract class ResourceServerConfig extends ResourceServerConfigurerAdapter { private static final String RESOURCE_ID = "resource-server-rest-api"; @Autowired TokenStore tokenStore; @Override public void configure(ResourceServerSecurityConfigurer resources) { resources .resourceId(RESOURCE_ID) .tokenStore(tokenStore); } @Override public void configure(HttpSecurity http) throws Exception { http .csrf().disable() .cors().disable() .anonymous().disable() .requestMatchers() .antMatchers("/api/**").and() .authorizeRequests() .antMatchers("/api/secure").authenticated() .antMatchers("/api/unsecure").permitAll(); } }
But when I enable anonymous access in SecurityConfig and declare my unsecure url as permitAll, then I'm able to access that url.
.antMatchers("/api/unsecure", "/index.html", "/**.js", "/**.css", "/").permitAll()
My Controller class is as follows:
@RestController @RequestMapping("/api") public class DemoController { @GetMapping("/secure") public void sayHelloFriend() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); System.out.println("Current User: "+authentication.getName()); System.out.println("Hello Friend"); } @GetMapping("/unsecure") public void sayHelloStranger() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); System.out.println("Current User: "+authentication.getName()); System.out.println("Hello Stranger"); } }
Let me know if any more information is needed. Any help will be appreciated. But please keep in mind that its Spring Boot 2.0 not 1.5 as both have some critical differences as per my findings.