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
after the login, any endpoint accessed jumps to the root endpoint localhost:8082
.
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>
That works fine.
Now, when I use the OAuth 2.0 Server project with my webservice project, means I add the application properties from the demo webservice project into my application.properties
I cannot access any other endpoints but the root endpoint http://localhost:8082
.
I assume that the WebSecurityConfig
in my webservice project is the reason for that. It looks like this:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String ROLE_MYUSER = "MYUSER";
public static final String ROLE_MYADMIN = "MYADMIN";
private MyUserProperties myUserProperties;
public WebSecurityConfig(MyUserProperties myUserProperties) {
// Load usernames and passwords from properties file
this.myUserProperties = myUserProperties;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/abc/**").hasRole(ROLE_MYUSER)
.mvcMatchers(HttpMethod.GET, "/def1/{name}", "/def2/{name}").hasRole(ROLE_MYUSER)
.mvcMatchers(HttpMethod.PATCH, "/def/{name}", "/def2/{name}").hasRole(ROLE_MYUSER)
.antMatchers("/ghi/**").hasRole(ROLE_MYUSER)
//... and so on
.antMatchers("/**").hasRole(ROLE_MYADMIN)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.oauth2Login();
//.csrf().disable()
//.formLogin().disable();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails myuser = User
.withUsername(myUserProperties.getPortal().get("user"))
.password("{noop}" + myUserProperties.getPortal().get("pass"))
.roles(ROLE_MYUSER)
.build();
UserDetails myadmin = User
.withUsername(myUserProperties.getAdmin().get("user"))
.password("{noop}" + myUserProperties.getAdmin().get("pass"))
.roles(ROLE_MYUSER, ROLE_MYADMIN)
.build();
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
userDetailsManager.createUser(myuser);
userDetailsManager.createUser(myadmin);
return userDetailsManager;
}
}
I had Basic Auth
so far, I defined my two users with the two roles in a
@Bean
@Override
public UserDetailsService userDetailsService() {
Now switching to OAuth2 I removed in
@Override
protected void configure(HttpSecurity http) throws Exception {
the lines
.httpBasic()
.csrf().disable()
.formLogin().disable()
and added
.oauth2Login()
The behavior in the web browser is the following
http://localhost:8082
delivers correctly the data from that root endpoint, without any login.
Any other endpoint, like for example
http://localhost:8082/abc
first leads to the login page, where I enter the defined user john / doe
from the OAuth 2.0 Server project, after that it doesn't show the expected data from the endpoint http://localhost:8082/abc
, but jumps back to the root endpoint http://localhost:8082
.
The first step I tried is to place
@Bean
@Override
public UserDetailsService userDetailsService() {
in the OAuth 2.0 Server project, into the class
@Configuration
@Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
but with that the login didn't work, it only accepted john / doe
.
The next stept was to remove
@Bean
@Override
public UserDetailsService userDetailsService() {
also from that class and define my two users in
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
where john / doe
was defined.
The login with one of my users works now, but the wrong behavior still remains. What else has to be altered, and where?
The access to the endpoints in my webservice project depends on the roles the user has.
My pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</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-oauth2-client</artifactId>
</dependency>
The solution here OAuth2 Client Principal do not have GrantedAuthorities when authenticated by Other Custom Authorization Server (SpringBoot2 & OAuth2)
solved it.
CustomOAuth2User
CustomOAuth2UserService
CustomAuthoritiesExtractor