So, I have an oAuth2 app which is jHipster app (using mongodb). I want to connect 3 resource apps to that app but all of them should share the same user base, so that the users should be able to login only once.
Is there a way to configure multiple resources in Spring Boot with jHipster so that it won't be as a separate client that would need username and password before accessing the resource?
And also how can I specify user role for each resource server?
All of the app are based on spring-boot.
The diagram below is a simplistic view of what I'm trying to accomplish.
So the OAuth2 app has has the Authorization Server configuration:
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Inject
private OAuth2AccessTokenRepository oAuth2AccessTokenRepository;
#Inject
private OAuth2RefreshTokenRepository oAuth2RefreshTokenRepository;
#Bean
public TokenStore tokenStore() {
return new MongoDBTokenStore(oAuth2AccessTokenRepository,
oAuth2RefreshTokenRepository);
}
#Inject
#Qualifier("authenticationManagerBean")
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("app-auth")
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800))
.and()
.withClient("app-A")
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN,AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800))
.and()
.withClient("app-A")
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN,AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800))
.and()
.withClient("app-C")
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN,AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment,
ENV_OAUTH);
}
}
As well the OAuth2 app has has the Resource Server configuration:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(
new AntPathRequestMatcher("/oauth/authorize"))
.disable().headers().frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers("/api/authenticate")
.permitAll().antMatchers("/api/register").permitAll()
.antMatchers("/api/logs/**")
.hasAnyAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").authenticated()
.antMatchers("/metrics/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/health/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/dump/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/shutdown/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/beans/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/configprops/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/info/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api-docs/**")
.hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/protected/**").authenticated();
}
}
And the Resource Server on App A (which is almost the same for B and C):
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("app-A");
}
}
#EnableResourceServer annotation by default protects all your resources (except resources explicitly ignored or exposed by the AuthorizationEndpoint if there is an Authorization Server in the same application).
If you want to set up multiple Resource Servers in the same app you can do it in this way:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import java.util.Collections;
import java.util.List;
#Configuration
public class ResourceServersConfig {
#Bean
protected ResourceServerConfiguration adminResources() {
ResourceServerConfiguration resource = new ResourceServerConfiguration() {
public void setConfigurers(List<ResourceServerConfigurer> configurers) {
super.setConfigurers(configurers);
}
};
resource.setConfigurers(Collections.<ResourceServerConfigurer>singletonList(new ResourceServerConfigurerAdapter() {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("admin-resources");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/rest/admin/**").authorizeRequests().anyRequest()
.access("#oauth2.hasScope('administration') and #oauth2.clientHasRole('admin')");
}
}));
resource.setOrder(3);
return resource;
}
#Bean
protected ResourceServerConfiguration userResources() {
ResourceServerConfiguration resource = new ResourceServerConfiguration() {
public void setConfigurers(List<ResourceServerConfigurer> configurers) {
super.setConfigurers(configurers);
}
};
resource.setConfigurers(Collections.<ResourceServerConfigurer>singletonList(new ResourceServerConfigurerAdapter() {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("user-resources");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/rest/user/**").authorizeRequests().anyRequest()
.access("#oauth2.hasAnyScope('offer','order') and #oauth2.clientHasRole('user')");
}
}));
resource.setOrder(4);
return resource;
}
}
Please take a look at Dave Syer's example.
Related
i am new to Spring Boot and OAuth2 , i found ressources on github and trying to practice to understand more the architecture and flows, so i have the configuration as follow :
OAuth2Configuration.java
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/secure/**", "/person/**", "/product/**").authenticated()
.antMatchers(HttpMethod.GET, "/user/**").authenticated()
.antMatchers(HttpMethod.PUT, "/user/**").authenticated()
.antMatchers(HttpMethod.DELETE, "/user/**").authenticated()
.antMatchers(HttpMethod.POST, "/user").permitAll();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
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(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.redirectUris("http://localhost:8080/login")
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
// Define the type of encode
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
//.antMatchers("/h2console/**")
.antMatchers("/register")
.antMatchers("/activate")
.antMatchers("/lostpassword")
.antMatchers("/resetpassword")
//.antMatchers("/hello")
.antMatchers("/person")
.antMatchers("/product");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}
CustomAuthenticationEntryPoint.java
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
log.info("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
what i want to achieve is authenticate users using loging form on browser in order to access protected ressources , but i don't know how in this configuration.
example :
when i access to /product , it shows all products cos it's not secured , but /product/3 for example is protected so it shows a blank webpage with error access denied , i want to show loging form.
when
I'm trying to implement an authentication success handler in spring security but it is never called (authenticationSuccessHandler).
This is my HttpSecurity configuration
#Configuration
public class OAuth2ServerConfiguration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
#Inject
private MyLoginSuccessHandler authenticationSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.and()
.csrf()
.disable()
.headers()
.frameOptions().disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/register").permitAll()
.antMatchers("/health/**").permitAll()
.antMatchers("/api/logs/**").hasAnyAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").authenticated()
.antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/liquibase/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/protected/**").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Inject
private DataSource dataSource;
#Inject
private JHipsterProperties jHipsterProperties;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Inject
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(jHipsterProperties.getSecurity().getAuthentication().getOauth().getClientid())
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token", "authorization_code", "implicit")
.secret(jHipsterProperties.getSecurity().getAuthentication().getOauth().getSecret())
.accessTokenValiditySeconds(jHipsterProperties.getSecurity().getAuthentication().getOauth().getTokenValidityInSeconds());
}
}
and this is my custom implementation
#Component
public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {
private static final Logger LOG =
LoggerFactory.getLogger(MyLoginSuccessHandler.class);
public static final String BEARER_AUTHENTICATION = "Bearer ";
#Inject
private TokenStore tokenStore;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
LOG.info("YEAH BABY !!");
}
}
The onAuthenticationSuccess of my MyLoginSuccessHandler class is never called.
Has someone any idea why ?
Thanks for your help.
I am using Oauth2 with spring boot 1.5.2.RELEASE. When I am trying to override the configure method of the ResourceServerConfigurerAdapter class it gives me a compilation error. But this is working fine with Spring boot 1.2.6.RELEASE.
Below is my code,
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/hello/").permitAll()
.antMatchers("/secure/**").authenticated();
}
Above code is working fine in the Spring Boot 1.2.6 but there is a compilation error when I try to call sessionManagement() method in 1.5.2 version. I guess the method has been removed in the new version.
But when I try with disable().and().sessionManagement() the compilation error removes but authentication is not working as expected. Can anybody help me to resolve this.
Below is my full code
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableResourceServer
#ComponentScan(basePackages = "security")
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/hello/").permitAll()
.antMatchers("/secure/**").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
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(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/h2console/**")
.antMatchers("/api/register")
.antMatchers("/api/activate")
.antMatchers("/api/lostpassword")
.antMatchers("/api/resetpassword")
.antMatchers("/api/hello");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
log.info("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
According to Spring Boot 1.5 Release Notes:
OAuth 2 Resource Filter
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 just add security.oauth2.resource.filter-order = 3 to your application.properties would solve this problem.
Yes. The API is bit changed. sessionManagement method can be invoked with a reference of HttpSecurity.
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/hello/").permitAll()
.antMatchers("/secure/**").authenticated();
However you haven't provided enough information to resolve your authentication issue. An answer given to the following problem can be able to resolve your problem.
Spring boot Oauth 2 configuration cause to 401 even with the permitall antMatchers
I'm using spring OAuth2 loginForm and access_token way to authenticate. But when I login in, I can not get access to resource server which needs access_token authorization.
How can I get access_token when I login in?
Should I create access_token by myself manually?
What I config with spring security is:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SpringDataMyBatisUserDetailsService userDetailsService;
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.userDetailsService)
.passwordEncoder(Manager.PASSWORD_ENCODER);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(
"/druid/**",
"/images/**"
);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
}
#Order(1)
#Configuration
#EnableAuthorizationServer
public static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private SpringDataMyBatisClientDetailsService clientDetailsService;
#Autowired
public AuthorizationServerConfig(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* Defines the security constraints on the token endpoints /oauth/token_key and /oauth/check_token
* Client credentials are required to access the endpoints
*
* #param oauthServer
* #throws Exception
*/
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
// .passwordEncoder(Client.PASSWORD_ENCODER)
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
/**
* Defines the authorization and token endpoints and the token services
*
* #param endpoints
* #throws Exception
*/
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(this.authenticationManager)
.tokenEnhancer(tokenEnhancer())
.tokenStore(tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.withClientDetails(clientDetailsService);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
#Order(3)
#Configuration
#EnableResourceServer
public static class ApiResources extends ResourceServerConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
private AuthenticationSuccessHandler successHandler;
#Autowired
private AuthenticationFailureHandler failureHandler;
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenStore(tokenStore);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.and()
.logout();
}
}
#Order(4)
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/authention/login")
.defaultSuccessUrl("/", true)
.failureUrl("/authention/login?error")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/authention/login?success")
.and()
.sessionManagement()
.sessionFixation().migrateSession();
}
}
#Bean
public static AuthenticationSuccessHandler myAuthenticationSuccessHandler() {
return new SavedRequestAwareAuthenticationSuccessHandler();
}
#Bean
public static AuthenticationFailureHandler myAuthenticationFailureHandler() {
return new SavedRequestAwareAuthenticationFailureHandler();
}
}
When you configure spring-oauth in your app you can access REST APIs to get the tokens, revoke tokens etc.
See this link For basic oauth configuration for a spring boot application. And also go through the API reference
Sample OAuth2AuthorizationServerConfig :
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("hello")
.authorizedGrantTypes("password", "refresh_token")
.authorities("ROLE_APP")
.scopes("read", "write")
.secret("secret");
}
}
And the SecurityConfig class:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackages = {"com.test.config"})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable();
http
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')")
.and()
.exceptionHandling()
.authenticationEntryPoint(oauthAuthenticationEntryPoint())
.accessDeniedHandler(oAuth2AccessDeniedHandler());
http
.formLogin()
.loginPage("/login")
.failureUrl("/")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login")
.permitAll();
}
}
After you configure your application. You can access the REST API as below.
For getting the token you need to access this URL:
localhost:8080/oauth/token?grant_type=password&client_id=hello&client_secret=secret&username=admin&password=password
This will authenticate the user if it is successful then the token is generated as shown below:
{
"access_token": "0307d70f-e3da-40f4-804b-f3a8aba4d8a8",
"token_type": "bearer",
"refresh_token": "daf21f97-f425-4245-8e47-19e4c87000e8",
"expires_in": 119,
"scope": "read write"
}
After getting this token you can access the REST APIs of your application just by passing this token. For example, if you have a URL "/hello" then
put a request appending the token which you obtained through above step.
"http://localhost:8080/hello?access_token=0307d70f-e3da-40f4-804b-f3a8aba4d8a8"
I need to add additional restriction to authenticate users. My User model has a field 'active'. It is false, when User registers, but not activate his account with hash from mail. Now, even if User is not active he gets access_token from Oauth.
How should I configure this?
I was thinking about SpringSecurityInterceptor, but I'm not sure about confusing Spring Security with OAuth2.
This is my SpringOAuth2.0 configuration:
#Configuration
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
private static final String RESOURCE_ID = "restservice";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/rest/**")
))
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
UserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService)
.pathMapping("/oauth/token", "/rest/oauth/token");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("USER")
.scopes("read", "write", "trust")
.resourceIds(RESOURCE_ID)
.secret("123456");
}
}
}
And also Spring security
#Configuration
#Order(2147483640)
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
UserDetailsService userDetailsService;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("email")
.passwordParameter("password")
.defaultSuccessUrl("/user/")
.successHandler(successHandler())
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(86400)
.and()
.csrf().disable();
}
#Bean
public AuthenticationSuccessHandler successHandler() {
return new UserLoginSuccessHandler();
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
#Bean
public SpringSecurityDialect securityDialect() {
return new SpringSecurityDialect();
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Any advice would be helpful.
If I understand you correctly, you don't want your authorization server to grant an access token for a user that is not activated?
You could let your UserDetailsService.loadUserByUsername throw a UsernameNotFoundException if the user exists, but is not activated.