Spring Boot 2.5.3 OAuth2 - Auth-Server and Webservice separate, Login error - spring-boot

Following the example on https://developer.okta.com/blog/2019/03/12/oauth2-spring-security-guide using the projects Create an OAuth 2.0 Server and Build Your Client App I cannot get it running without error.
I don't use Thymeleaf, as my Webservice returns data, not a page.
OAuth 2.0 Server project
#SpringBootApplication
#EnableResourceServer
public class Demo2Application {
public static void main(String[] args) {
SpringApplication.run(Demo2Application.class, args);
}
}
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final PasswordEncoder passwordEncoder;
public AuthorizationServerConfig(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("abcd")
.secret(passwordEncoder.encode("fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9"))
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true)
.redirectUris("http://localhost:8082/login/oauth2/code/");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");;
}
}
#Configuration
#Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("john")
.password(passwordEncoder().encode("doe"))
.roles("USER");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
#RestController
public class UserController {
#GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
}
application.properties
server.port=8090
pom.xml
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
I omit the context path, which the project originally uses.
Webservice project
#RestController
public class MyRESTController {
#GetMapping("/securedPage")
public String securedPage(Principal principal) {
return "securedPage";
}
#GetMapping("/")
public String index(Principal principal) {
return "index";
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers("/", "/login**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login();
}
}
application.properties
server.port=8082
server.servlet.session.cookie.name=UISESSION
spring.security.oauth2.client.registration.custom-client.client-id=abcd
spring.security.oauth2.client.registration.custom-client.client-secret=fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9
spring.security.oauth2.client.registration.custom-client.client-name=Auth Server
spring.security.oauth2.client.registration.custom-client.provider=custom-provider
spring.security.oauth2.client.registration.custom-client.scope=user_info
spring.security.oauth2.client.registration.custom-client.redirect-uri=http://localhost:8082/login/oauth2/code/
spring.security.oauth2.client.registration.custom-client.client-authentication-method=basic
spring.security.oauth2.client.registration.custom-client.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.custom-provider.authorization-uri=http://localhost:8090/oauth/authorize
spring.security.oauth2.client.provider.custom-provider.token-uri=http://localhost:8090/oauth/token
spring.security.oauth2.client.provider.custom-provider.user-info-uri=http://localhost:8090/user/me
spring.security.oauth2.client.provider.custom-provider.user-name-attribute=name
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
When running both porojects, with localhost:8082 in the web browser I get index as response.
With localhost:8082/securedPage I get redirected to the login page, entering username john and password doe I get the following error page:
Login with OAuth 2.0
[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 404 : [{"timestamp":"2021-07-30T07:58:54.529+00:00","status":404,"error":"Not Found","path":"/user/me"}]
Auth Server
Don't know what the error is causing. Looks like it is related to the Webservice application property and the URL to the UserController in the OAuth 2.0 Server project
spring.security.oauth2.client.provider.custom-provider.user-info-uri=http://localhost:8090/user/me
One additional question is:
Can the login form be avoided? The credentials passed somehow.
If the client is not the web browser but some other application which has no UI. Or perhaps curl. I also have MockMvc tests, so far it was Basic Auth, now OAuth2, how will be those affected?

Related

How to configure oAuth2 when Authorization Server is also the Resource server

I'm trying to setup a very basic oAuth2 authentication in spring boot 2.x.x using either authorization code grant or implicit grant but I can't seem to access the Resource server (which resides in the same spring boot app as the Authorization server) after the token is obtained.
Following is the configuration of WebSecurityConfigurerAdapter
#EnableWebSecurity
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String[] IGNORE_URIS = {
"/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**",
"/resources/**",
"/h2-console/**",
"/common/**",
"/configuration/ui",
"/configuration/security",
"/error"
};
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(IGNORE_URIS);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/product/**")
.hasAnyRole("ADMIN").and()
.httpBasic().and().formLogin().and().authorizeRequests().anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("ADMIN");
}
#Bean
public PasswordEncoder bCrypt() {
return new BCryptPasswordEncoder();
}
And the AuthorizationServerConfigurerAdapter
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServerConfiguration(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("my-client-id")
.authorizedGrantTypes("authorization_code", "implicit")
.authorities("ADMIN")
.scopes("all")
.resourceIds("product_api")
.secret("{noop}secret").redirectUris("https://google.com").accessTokenValiditySeconds(0);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
So far so good. I am able to reach the default Spring login page by typing the following Url in the browser.
http://localhost:8080/oauth/authorize?response_type=token&client_id=my-client-id&redirect_uri=https://google.com
Then The login page shows up and I enter my credentials.
After I log in I can then grant access to "my-client-id" app.
Eventually after I approve the app I can see the newly generated access token in the URL bar of the browser which is something like this.
https://www.google.com/#access_token=f2153498-6a26-42c6-93f0-80825ef03b16&token_type=bearer&scope=all
My question is that All of this flow won't work when I also configure a Resource Server.
#EnableResourceServer
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("product_api");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/**")
.and().authorizeRequests()
.antMatchers("/**").permitAll();
}
}
What am I doing wrong? When I try to access the oauth/authorize url as before I get the following:
Why? How can one access the login page and retrieve the token? What Am I missing?
You need to use
#Order
Annotation to specify order for WebMvc and ResourceServer classes
#EnableWebSecurity
#Configuration
#Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
and for Resource Server
#EnableResourceServer
#Configuration
#Order(2)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
...
}
If you want to see workable example, you can check it here https://github.com/alex-petrov81/stackoverflow-answers/tree/master/auth-server-also-resource
I've created it from your code example.

Spring Security inmemory authentication issue

I am using Spring Security inmemory authentication, but it is not working properly.
It is generating default password but it is not taking the user and password I generated in configuration. Below is my controller:
#CrossOrigin(origins = "*")
#EnableAutoConfiguration
#RestController
#RequestMapping("/api")
public class AppController {
#RequestMapping(value="/hello", method=RequestMethod.GET, produces="text/plain")
public String sayHello() {
return "Hello welcome to spring security!!!!";
}
}
And below is my security configuration class:
#Component
#EnableWebSecurity
/*#EnableGlobalMethodSecurity*/
#EnableAutoConfiguration(exclude = {
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class
})
public class SecurityProvider extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/*").hasRole("ADMIN")
.and()
.formLogin();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("ramu")
.password("password")
.roles("ADMIN")
.and()
.withUser("gopal")
.password("password")
.roles("USER");
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
// ALTHOUGH THIS SEEMS LIKE USELESS CODE,
// ITS REQUIRED TO PREVENT SPRING BOOT AUTO-CONFIGURATION
return super.authenticationManagerBean();
}
}
I also tried by excluding SecurityAutoconfiguration.class at main application by using below annonation:
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
public class App {
public static void main( String[] args ) {
SpringApplication.run(App.class, args);
}
}
But no luck, it is not taking the username and password I configured in my configuration class.
I fixed your setup, see the code below.
The WebSecurityConfig (instead of the former ServiceProvider component):
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/api/*").hasRole("ADMIN").and().formLogin();
}
PasswordEncoder passwordEncoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("ramu")
.password("password")
.roles("ADMIN")
.and()
.passwordEncoder(passwordEncoder)
.withUser("gopal")
.password("password")
.roles("USER");
}
}
The RestController:
#CrossOrigin(origins = "*")
#EnableAutoConfiguration
#RestController
#RequestMapping("/api")
public class AppController {
#RequestMapping(value="/hello",method=RequestMethod.GET,produces="text/plain")
public String sayHello(){
return "Hello welcome to spring security!!!!";
}
}
What I had to change:
ServiceProvider #Component -> WebSecurityConfig #Configuration
I registered a PasswordEncoder
I reverted all of the #EnableAutoConfiguration annotations
I reverted the AuthenticationManager Bean
I used the spring-parent:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
Solution for spring-boot-starter-parent version 1.5.7.RELEASE:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/api/*").hasRole("ADMIN").and().formLogin();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("ramu")
.password("password")
.roles("ADMIN")
.and()
.withUser("gopal")
.password("password")
.roles("USER");
}}
Maybe consider to use a kind of passwordEncoder here as well.

Spring oauth2 and digest authentication will work together

I have added spring oauth2 into my restful service. Most of the services are consumed by my own portal so getting the token then calling the api is fine. However i have exposed some more web services which has to be called without this token concept. Those consumers have username and password.
The best example is Swagger implementation. Where opening the swagger page should be authenticated via digest instead of oauth token.
The below code changes i made but it is not working.
I believe, i no need to call oauth server from the resource server in this case. So just made the below code within the resource server. But seen the problem like after authentication page accepted my credentials it is again rerouting/redirecting to the same authentication page.
Please help.
#Configuration
#EnableResourceServer
public class ResourceServerImpl extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().requestMatchers().antMatchers("/**").and().authorizeRequests()
.antMatchers(HttpMethod.POST, "/url1/path1/path2").hasAnyAuthority( "FUNCTION_FN1")
.antMatchers(HttpMethod.GET, "/url2/path1/path2").hasAnyAuthority( "FUNCTION_FN2")
.antMatchers("/swagger-ui.html").hasRole("USER")
.anyRequest().authenticated()
.and().formLogin().and().httpBasic()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
UPDATE
I dont think this is possible to make some request to be authenticated with DIGEST and some are with OAUTH. So currently i made swagger url also to be authenticated with oauth token, instead of digest support.
Yes, it's definitely possible to configure multiple authorization mechanisms in the same application. Distinct instance of WebSecurityConfigurerAdapter should be provided for each mechanism.
#EnableResourceServer and #EnableAuthorizationServer provide corresponding oauth adapters. ApiSecurityConfiguration provides adapter for basic auth.
Here is minimal working sample for basic and oauth2:
#SpringBootApplication
public class TestApp {
#RestController
public static class Endpoints {
// doesn't require any authentication
#RequestMapping("/test")
public String test() {
return "no auth";
}
// requires basic authentication
#RequestMapping("/api/test")
public String apiTest() {
return "basic auth";
}
// requires oauth authentication
#RequestMapping("/oauth/test")
public String oauthTest() {
return "oauth";
}
}
#Configuration
#EnableWebSecurity
public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests().antMatchers("/api/**").hasRole("USER")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
}
#Configuration
#EnableAuthorizationServer
public static class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.secret("test")
.authorizedGrantTypes("client_credentials")
.authorities("USER")
.scopes("read", "write", "test")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(120);
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/oauth/**")
.and()
.authorizeRequests().antMatchers("/oauth/**").authenticated();
}
}
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
}
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>

Spring Boot Swagger UI - Protect UI Access

I added a simple swagger UI to my existing springboot REST API by adding the following class to my code:
#EnableSwagger2
#Configuration
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.regex("/v1.*"))
.build()
.pathMapping("/")
.apiInfo(metadata());
}
private ApiInfo metadata() {
return new ApiInfoBuilder()
.title("My awesome API")
.description("Some description")
.version("1.0")
.build();
}
}
My problem is that the API should be public, but the swagger docs should not. I would like a way of requesting authentication to the swagger documentation, anyone knows any simple way of achieving this?
I tried to google it but I could only find OAth stuff, but this is authentication for the endpoints not the swagger documentation...
Swagger docs will be available at /v2/api-docs endpoint when swagger integrated with spring boot application.
Inorder to protect the resource , make use of spring security and restrict the endpoint for accessing the docs
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Security configuration : restricting access to the endpoint only to the users
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/v2/api-docs").authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
Additionally, swagger-ui.html can also be secured based on the requirement.
Here's a an alternative solution. This is about limiting access to swagger only in development/qa environment. The production environment will not have access to Swagger. I am using a property (prop.swagger.enabled) as a flag to bypass spring security authentication for swagger-ui only in development/qa environment.
#Configuration
#EnableSwagger2
public class SwaggerConfiguration extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
#Value("${prop.swagger.enabled:false}")
private boolean enableSwagger;
#Bean
public Docket SwaggerConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(enableSwagger)
.select()
.apis(RequestHandlerSelectors.basePackage("com.your.controller"))
.paths(PathSelectors.any())
.build();
}
#Override
public void configure(WebSecurity web) throws Exception {
if (enableSwagger)
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (enableSwagger) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
}
I use this boiler plater to configure and secure swagger
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebSecurityConfigurerAdapter {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**")
.authenticated().and().httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}

spring boot oauth2 configuration: resource server remains unprotected

I have implemented authorization server and resource server using spring boot. authorization server works fine and I am able to get tokens. But my resource server remains unprotected. My objective is that resource server ahould only be accessed by someone having valid access token.
My entire code is:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
TokenStore tokenStore;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.scopes("read", "write")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.authorizedGrantTypes("password", "refresh_token")
.secret("secret")
.accessTokenValiditySeconds(180)
.refreshTokenValiditySeconds(600);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security); //To change body of generated methods, choose Tools | Templates.
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenServices(tokenServices())
.resourceId("MY_RESOURCE");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.requestMatchers().antMatchers("/**")
.and()
.authorizeRequests()
.antMatchers("/").access("hasRole('USER')")
.antMatchers("/secure/").access("hasRole('ADMIN')")
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore);
return defaultTokenServices;
}
}
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("bill").password("abc123").roles("ADMIN").and()
.withUser("bob").password("abc123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
}
}
#Configuration
#EnableGlobalMethodSecurity
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
#SpringBootApplication
#RestController
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#GetMapping(value = "/")
public ResponseEntity<?> hello(){
return ResponseEntity.ok("Hello World");
}
#GetMapping(value = "/secure/")
public ResponseEntity<?> secure(){
return ResponseEntity.ok("Secure Resorce");
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>boot-oauth2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>boot-oauth2</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
What am I missing?
Thanks for help.
UPDATE:
I figured out that my resource server is unprotected because of presence of OAuth2SecurityConfig class. If I remove this class and add following class (where I have moved the inMemmory users), then resource server is protected as required
#Configuration
public class WebSecurityGlobalConfig extends GlobalAuthenticationConfigurerAdapter {
#Autowired
UserService userService;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("bill").password("abc123").roles("ADMIN").and()
.withUser("bob").password("abc123").roles("USER");
}
}
So, I am sensing improper HttpSecurity configuration in OAuth2SecurityConfig class is conflicting with resource server config.
So, How can I configure HttpSecurity of OAuth2SecurityConfig so, that it does allow access token protection for resource server paths and normal web security for non-resource server paths
finally after a lot of googling, I found the solution.
It was due to order of filters. order of OAuth2 resource filter has been changed in spring-boot-1.5.1. as change log says
The default order of the OAuth2 resource filter has changed from 3 to
SecurityProperties.ACCESS_OVERRIDE_ORDER - 1. This places it after the
actuator endpoints but before the basic authentication filter chain.
The default can be restored by setting
security.oauth2.resource.filter-order = 3
So, I changed the order of my OAuth2 resource server filter to 3 by setting it in application.properties security.oauth2.resource.filter-order = 3 and my problem was solved.
Annotate your OAuth2SecurityConfig with #EnableGlobalMethodSecurity(prePostEnabled = true)
I had the same issue.
I had another class extending WebSecurityConfigurerAdapter that i guess was in conflict with AuthorizationServerConfigurerAdapter.
I just removed the WebSecurityConfigurerAdapter class and it worked.

Resources