I am getting access denied on accessing request based on role. I don't know why. I am using spring security with oauth2 in spring boot.
The configured authorization server is -
#EnableAuthorizationServer
#Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Autowired
private AuthConfig config;
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient(config.getClientId()).secret("{noop}".concat(config.getClientSecret()))
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(config.getAccessTokenValidity())
.refreshTokenValiditySeconds(config.getRefresTokenValidity());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.pathMapping("/oauth/token", config.getAuthPath());
endpoints.authenticationManager(authManager).tokenStore(tokenStore()).accessTokenConverter(jwtTokenEnhancer());
endpoints.userDetailsService(userDetailsService);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtTokenEnhancer());
}
#Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"),
config.getKeyStorePassword().toCharArray());
JwtAccessTokenConverter converter = new CustomTokenEnhancer();
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
return converter;
}
}
and resource server is configured as
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/create/user").permitAll().antMatchers("/hello").hasRole("superadmin")
.anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
}
and sever security configured as
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
and decoded data in generated token is
{
"exp": 1547123578,
"user_name": "superadmin",
"authorities": [
{
"authority": "ROLE_superadmin"
}
],
"jti": "e1f6e67c-16b8-4a12-a300-fae7f406359e",
"client_id": "pgcil",
"scope": [
"read",
"write"
]
}
but http request http://localhost:8089/hello with jwt token gives acccess denied error. May anyone tell me what i am doing wrong.Any help would be appreciated.
Related
Im trying to implement Authorisation server with password and client_credentials grant
#Configuration
#EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
public PasswordEncoder passwordEncoder;
#Autowired
private DataSource dataSource;
#Autowired
private TokenStore jwtTokenStore;
#Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
#Autowired
private TokenEnhancer jwtTokenEnhancer;
#Bean
public TokenEnhancer jwtTokenEnhancer(){
return new JWTokenEnhancer();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer(), jwtAccessTokenConverter));
endpoints
.authenticationManager(authenticationManager)
.tokenStore(jwtTokenStore)
.accessTokenConverter(jwtAccessTokenConverter)
.tokenEnhancer(enhancerChain);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
JdbcClientDetailsServiceBuilder jcsb = clients.jdbc(dataSource);
jcsb.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.passwordEncoder(passwordEncoder)
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
}
web config file
Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/api-docs/**").permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
// Allow eureka client to be accessed without authentication
web.ignoring().antMatchers("/*/")//
.antMatchers("/eureka/**")//
.antMatchers(HttpMethod.OPTIONS, "/**"); // Request type options should be allowed.
}
}
#Configuration
public class JwtTokenConfig {
#Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("dev");
return accessTokenConverter;
}
}
i have configured client details to pick up from database -
When i try to get access token based on password grant im able to get the access token
but when i try to get access token based on grnat_type client credentials - im getting internal server error .
Please help to check on what is wrong with my implementation.
enter image description here
In your class OAuthConfiguration, check client configuration present in configure(ClientDetailsServiceConfigurer clients) method. It appears that the JDBC client details service is not able to find any client details.
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
jdbcClientDetailsService.listClientDetails(); // This probably would be empty.
If so, configure JDBC client details service something like this:
clients.jdbc(dataSource).withClient(CLIEN_ID)
.secret(encoder.encode(CLIENT_SECRET))
.authorizedGrantTypes("password", "refresh_token", "client_credentials")
.scopes("read", "write")
.resourceIds(RESOURCE_ID).and().build();
Found the Issue .
public class JWTokenEnhancer implements TokenEnhancer{
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> info = new HashMap<>();
info.put("user-info", "user additional information...");
// User user = (User) authentication.getPrincipal();
// info.put("isAdmin", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()).contains("ROLE_ADMIN"));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
return accessToken;
}
The commented line was causing the issue as there was no user in case of client_credentials
I am trying to implement OAuth2 - SpringBoot authentication.
I have configured a path with permitAll(), but even though it is configured, it shows error
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
I am using postman to test and simply trying to fetch all users in DB. When I call, the control is not coming to RestController. I would like to just get the users list and permitAll() is provided.
Can anyone please help ?
I am posting the code below.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().
antMatchers(HttpMethod.POST, "/api/**").permitAll().
antMatchers(HttpMethod.POST,"/admin/**").hasAnyRole("ADMIN").
anyRequest().authenticated();
}
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception{
builder.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select usrnam,usrpwd, case when usrsta='A' then true else false end from chsusrmst where usrnam=?")
.authoritiesByUsernameQuery("select usrnam,usrtyp from chsusrmst where usrnam=?");
}
}
#RestController
#RequestMapping("/api")
public class UserController {
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private UserRepository userRepository;
#PostMapping("/user/register")
public String register(#RequestBody User user) {
String encodedPassword = passwordEncoder.encode(user.getUserPassword());
user.setUserPassword(encodedPassword);
userRepository.save(user);
return "User created";
}
#PostMapping("/admin/findUser")
public User findUser(#RequestBody User user) {
return userRepository.findByUserName(user.getUserName());
}
#PostMapping("/user/findAllUsers")
public List<User> findAllUsers() {
return userRepository.findAll();
}
}
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final PasswordEncoder passwordEncoder;
private final UserDetailsService userDetailsService;
#Value("${jwt.clientId:client}")
private String clientId;
#Value("${jwt.client-secret:secret}")
private String clientSecret;
#Value("${jwt.signing-key:123}")
private String jwtSigningKey;
#Value("${jwt.accessTokenValidititySeconds:43200}") // 12 hours
private int accessTokenValiditySeconds;
#Value("${jwt.authorizedGrantTypes:password,authorization_code,refresh_token}")
private String[] authorizedGrantTypes;
#Value("${jwt.refreshTokenValiditySeconds:2592000}") // 30 days
private int refreshTokenValiditySeconds;
public AuthorizationServerConfig(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
this.userDetailsService = userDetailsService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.secret(passwordEncoder.encode(clientSecret))
.accessTokenValiditySeconds(accessTokenValiditySeconds)
.refreshTokenValiditySeconds(refreshTokenValiditySeconds)
.authorizedGrantTypes(authorizedGrantTypes)
.scopes("read", "write")
.resourceIds("api");
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.accessTokenConverter(accessTokenConverter())
.userDetailsService(userDetailsService)
.authenticationManager(authenticationManager);
}
#Bean
JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
return converter;
}
}
#Configuration
#EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer serverSecurityConfigurer) {
serverSecurityConfigurer.resourceId("api");
}
}
Thanks for your consideration. I found the issue. HttpSecurity configuration was missing in Resource server and it has been resolved by adding below section.
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/user**").permitAll()
.antMatchers("/user/**").permitAll()
.antMatchers("/admin**").hasAuthority("ADMIN")
.antMatchers("/api/**").authenticated()
.anyRequest().authenticated();
I am trying to develop a rest api with spring security Using OAuth2 implementation. but how do I remove basic authentication. I just want to send a username and password to body and get a token on postman.
#Configuration
public class OAuthServerConfigration {
private static final String SERVER_RESOURCE_ID = "oauth2-server";
private static InMemoryTokenStore tokenStore = new InMemoryTokenStore();
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(SERVER_RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().requestMatchers().antMatchers("/api/**").and().authorizeRequests().antMatchers("/api/**").access("#oauth2.hasScope('read')");
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret("$2a$10$5OkeCLKNs/BkdO0qcYRri.MdIcKhFvElAllhPgLfRQqG7wkEiPmq2")
.authorizedGrantTypes("password","authorization_code","refresh_token")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds(SERVER_RESOURCE_ID)
//.accessTokenValiditySeconds(ONE_DAY)
.accessTokenValiditySeconds(300)
.refreshTokenValiditySeconds(50);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
// we're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
}
}
#Configuration
#Order(2)
public static class ApiLoginConfig extends
WebSecurityConfigurerAdapter{
#Autowired
DataSource dataSource;
#Autowired
ClientDetailsService clientDetailsService;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().csrf().disable().antMatcher("/oauth/token").authorizeRequests().anyRequest().permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#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;
}
}
want to remove the basic authentication and send the username password in the body tag from the postman for get token
and I have got some problem
{
"error": "unauthorized",
"error_description": "There is no client authentication. Try adding an appropriate authentication filter."
}
In your #EnableAuthorizationServer configuration class in the method:-
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
Try to add the following:-
oauthServer.allowFormAuthenticationForClients()
After you have done that you will have to call the oauth get token url as below:-
URL will be the same as http(s)://{HOST_NAME}/oauth/token
HTTP method type now will be POST
Header:-
Content-Type=application/x-www-form-urlencoded
Parameters will be key value pairs in x-www-form-urlencoded in the body of postman
for client_credentials grant_type:-
grant_type=client_credentials
client_id=client_id_value
client_secret=client_secret_value
scope=scopes
for password grant_type:-
grant_type=password
client_id=client_id_value
client_secret=client_secret_value
scope=scopes
username=username
password=password
scopes will be comma separated here
In my system I use JWT tokens so that authorization of requests could be performed based on JWT only, with no need to call database to fetch roles etc. I use OAuth, and my services are Resource servers which works fine and I'm satisfied with that. The problem I have is with my uaa-service, which is both Authorization and Resource server.
I've noticed that when I send requests to uaa-service I can inject #AuthenticatedPrincipal into my controller method and I have access to all user fields, not only those which are present in JWT. It means that Spring somehow maintains session or maybe in the background fetches user data from database. Here are my settings:
#Configuration
#EnableResourceServer
#Slf4j
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
log.info("Configuring resource server");
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/register").permitAll().anyRequest().authenticated();
}
#Autowired
public void setJwtAccessTokenConverter(JwtAccessTokenConverter jwtAccessTokenConverter) {
jwtAccessTokenConverter.setAccessTokenConverter(bitcoinTokenConverter());
}
#Bean
DefaultAccessTokenConverter bitcoinTokenConverter() {
return new CustomTokenConverter();
}
And
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource(keystoreName), keystorePassword.toCharArray())
.getKeyPair(keystoreAlias);
converter.setKeyPair(keyPair);
return converter;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(customUserDetailsService)
.tokenEnhancer(tokenEnhancerChain);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
and
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Bean
FilterRegistrationBean forwardedHeaderFilter() {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(new ForwardedHeaderFilter());
filterRegBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filterRegBean;
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/register").permitAll().anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
auth.authenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher));
}
Where did I make a mistake? In my SecurityConfig.java I have
auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
so that the login could be performed by fetchining user from database and validating password but it looks like it may also cause that incoming requests are not handled only based on JWT.
I want to configure OAuth2 authentication for my application.
I have next configurations:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
#Configuration
#EnableAuthorizationServer
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String[] GRANT_TYPES = {"password", "refresh_token"};
private static final String[] SCOPES = {"read", "write"};
private final SecurityConfigurationProperties securityConfigurationProperties;
private final AuthenticationProvider authenticationProvider;
private final OAuth2AccessTokenRepository oAuth2AccessTokenRepository;
private final OAuth2RefreshTokenRepository oAuth2RefreshTokenRepository;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(securityConfigurationProperties.getClientId())
.authorizedGrantTypes(GRANT_TYPES)
.authorities(UserRole.USER.getName())
.scopes(SCOPES)
.secret(securityConfigurationProperties.getClientSecret())
.accessTokenValiditySeconds(securityConfigurationProperties.getAccessTokenTime())
.refreshTokenValiditySeconds(securityConfigurationProperties.getRefreshTokenTime());
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager())
.approvalStoreDisabled();
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
#Bean
public TokenStore tokenStore() {
return new MongoTokenStore(oAuth2AccessTokenRepository, oAuth2RefreshTokenRepository);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setAuthenticationManager(authenticationManager());
return tokenServices;
}
}
#Configuration
#EnableResourceServer
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "api";
private final TokenStore tokenStore;
#Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)
.tokenStore(tokenStore);
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.anonymous().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final ApiUserDetailsService apiUserDetailsService;
private final AuthenticationProvider authenticationProvider;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/").authenticated();
}
}
Also I have my custom AuthenticationProvider:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class UserAuthenticationProvider implements AuthenticationProvider {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
#Override
public Authentication authenticate(final Authentication authentication)
throws AuthenticationException {
final String email = authentication.getName();
final String password = authentication.getCredentials().toString();
return userRepository.findByEmail(email)
.filter(user -> passwordEncoder.matches(password, user.getPassword()))
.map(this::signInUser)
.orElseThrow(() -> new BadCredentialsException("Failed to authenticate"));
}
#Override
public boolean supports(final Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
private Authentication signInUser(final User user) {
final ApiUser springSecurityUser =
new ApiUser(user.getEmail(), user.getPassword(), user.getRoles());
final Authentication authentication = new UsernamePasswordAuthenticationToken(springSecurityUser,
user.getId(), springSecurityUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication;
}
}
Everything works for token I'm getting access and refresh tokens from /oauth/token endpoint, but when I'm trying to access resource with #PreAuthorize annotation I'm getting error.
Link for it http://localhost:8080/users/me?access_token=8450e2f3-2ecb-4e88-b304-685b22c2ad65 also I've tried to add "Authorization: Bearer 8450e2f3-2ecb-4e88-b304-685b22c2ad65" to headers
{
"timestamp": 1490358162182,
"status": 403,
"error": "Forbidden",
"exception": "org.springframework.security.authentication.AuthenticationCredentialsNotFoundException",
"message": "Access Denied",
"path": "/users/me"
}
My endpoint:
#PreAuthorize("hasRole('ROLE_USER')")
#RequestMapping(value = RestPath.Users.ME, method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity userInfo() {
return ResponseEntity.noContent().build();
}
Maybe someone had already such exception with same configuration.
Ok so the main problem in my configuration was in SecurityConfiguration class. I've added annotation #Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) according to this post https://stackoverflow.com/a/42836521/2055854.
Now it looks:
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
And I've also changed a bit my configurations:
#Configuration
public class OAuth2Config {
#Configuration
#EnableResourceServer
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public static class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "api";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER = "Bearer";
private static final String ACCESS_TOKEN = "access_token";
private final TokenStore tokenStore;
#Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)
.tokenStore(tokenStore);
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().anyRequest().permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
#Configuration
#EnableAuthorizationServer
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public static class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String[] GRANT_TYPES = {"password", "refresh_token"};
private static final String[] SCOPES = {"read", "write"};
private final SecurityConfigurationProperties securityConfigurationProperties;
private final AccessTokenRepository oAuth2AccessTokenRepository;
private final RefreshTokenRepository oAuth2RefreshTokenRepository;
private final AuthenticationProvider authenticationProvider;
private final UserDetailsService userDetailsService;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(securityConfigurationProperties.getClientId())
.authorizedGrantTypes(GRANT_TYPES)
.authorities(UserRole.USER.getName())
.secret(securityConfigurationProperties.getClientSecret())
.scopes(SCOPES)
.resourceIds(OAuth2ResourceServerConfig.RESOURCE_ID)
.accessTokenValiditySeconds(securityConfigurationProperties.getAccessTokenTime())
.refreshTokenValiditySeconds(securityConfigurationProperties.getRefreshTokenTime());
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager())
.userDetailsService(userDetailsService);
}
public AuthenticationManager authenticationManager() {
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
#Bean
public TokenStore tokenStore() {
return new MongoTokenStore(oAuth2AccessTokenRepository, oAuth2RefreshTokenRepository);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setAuthenticationManager(authenticationManager());
return tokenServices;
}
}
}
Now everything works as expected.