Spring Oauth2 not working for me - Status Not Answered - spring

I am trying out Oauth2 with Spring security. I am trying to call the /getresource service in my resource class by following the below
GET http://localhost:8080/oauth/authorize?
response_type=code
&client_id=my-first-client
&redirect_url=http://localhost:8080/getcode
&scope=read
Get the code and call the Authorization service to get the auth token by doing
POST http://localhost:8080/oauth/token
grant_type=authorization_code&code=XXXX
Get the token and call the /getresource rest service.
by
GET http://localhost:8080/getresource
Header : Authorization: Bearer <>
But I have been getting the below error
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
Any help will to locate the issue will be greatly appreciated.
My Authorization class
package auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
#EnableAuthorizationServer
#SpringBootApplication
#EnableWebSecurity
public class AuthProvider extends WebSecurityConfigurerAdapter implements AuthorizationServerConfigurer{
#Autowired
AuthenticationManager authenticationManager;
#Autowired
DriverManagerDataSource dataSource;
public static void main(String[] args)
{
SpringApplication.run(AuthProvider.class, args);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
String uname_query = "select username,userpass,enabled from users where username=?";
String role_query = "Select username, role_name from user_roles where username=?";
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(uname_query)
.authoritiesByUsernameQuery(role_query);
}
/* #Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/login","/home","/403","/oauth/**","/resources/**").permitAll()
.antMatchers("/getresource").access("#oauth2.hasScope('read')")
.anyRequest().authenticated().and().formLogin().loginPage("/login");
}*/
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// TODO Auto-generated method stub
security.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("my-first-client")
.secret("password")
.accessTokenValiditySeconds(120)
.authorizedGrantTypes("authorization_code", "refresh_token","password")
.authorities("ROLE_CLIENT")
.redirectUris("http://localhost:8080/authcode")
.resourceIds("testresource");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// TODO Auto-generated method stub
endpoints.authenticationManager(authenticationManager);
}
}
My Resource class
package auth;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#Configuration
#EnableResourceServer
#RestController
public class Resource {
private static final String resourceid="testresource";
#RequestMapping("/getresource")
public String getResource()
{
return "Hello Everyone!!";
}
private static class Resourceconfigurator extends ResourceServerConfigurerAdapter
{
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(Resource.resourceid);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/login","/home","/403","/oauth/**","/resources/**").permitAll()
.antMatchers("/getresource").access("#oauth2.hasScope('read')")
.anyRequest().authenticated().and().formLogin().loginPage("/login");
}
}
}

Related

Spring framework security bean "AuthenticationManager" not found

I am using OAuth2 with spring boot. I am new to Oauth. I am getting this Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration exception while running my spring boot application. I have seen some other answered questions in StackOverflow but they were no fulfilling my need. I am using Spring boot version 2.3.3.RELEASE. I am taking reference from this Repository. I have just Updated the Version of my application. Here is my class where i am facing this issue:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
#EnableAuthorizationServer
#Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("ClientId").secret("secret").authorizedGrantTypes("authorization_code")
.scopes("user_info").autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
If I create a bean like this. :
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
it has resolved my error for a class that extends WebSecurityConfigurerAdapter but my other class extends AuthorizationServerConfigurerAdapter and for this class, the solution is not working
and I am getting a warning saying The method authenticationManagerBean() is undefined for the type AuthorizationServerConfigurerAdapter. Could you please help me.
You are overriding a method that does not exist in AuthorizationServerConfigurerAdapter hense the error.
As you can see AuthorizationServerConfigurerAdapter define only configure method with 3 signatures:
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
I have resolved this issue by just updating my AuthorizationServerConfig.java class & ResourceServerConfig.java class like this:
AuthorizationServerConfig.java class:
package com.ab.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
#EnableAuthorizationServer
#Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("ClientId").secret("secret").authorizedGrantTypes("authorization_code")
.scopes("user_info").autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
ResourceServerConfig.java class:
package com.ab.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
#EnableResourceServer
#Configuration
public class ResourceServerConfig 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.parentAuthenticationManager(authenticationManagerBean()).inMemoryAuthentication().withUser("Peter")
.password("peter").roles("USER");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}

There was an unexpected error (type=Forbidden, status=403). Access Denied

There was an unexpected error (type=Forbidden, status=403). Access Denied.
When I was trying to access URL from postman or from browser I am getting an error of There was an unexpected error (type=Forbidden, status=403). Access Denied.
1)Web Security Class:-
import javax.servlet.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.photoapp.users.service.ApiUserService;
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private Environment environment;
private ApiUserService apiUserService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public WebSecurity(Environment environment , ApiUserService apiUserService , BCryptPasswordEncoder bCryptPasswordEncoder) {
this.environment = environment;
this.apiUserService = apiUserService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/**").hasIpAddress(environment.getProperty("gateway.ip"))
.and()
.addFilter(getAuthenticationFilter());
http.headers().frameOptions().disable();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception {
// TODO Auto-generated method stub
AuthenticationFilter authenticationFilter = new AuthenticationFilter();
authenticationFilter.setAuthenticationManager(authenticationManager());
return authenticationFilter;
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(apiUserService).passwordEncoder(bCryptPasswordEncoder);
}
}
2)Authentication Filter Class:-
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.photoapp.users.model.LoginRequestModel;
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public Authentication attemptAuthentiation(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
try {
LoginRequestModel creds = new ObjectMapper()
.readValue(req.getInputStream() , LoginRequestModel.class);
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken (
creds.getEmail(),
creds.getPassword(),
new ArrayList<>()));
}
catch(IOException e) {
throw new RuntimeException(e);
}
}
protected void succeddfulAuthentication(HttpServletRequest req, HttpServletResponse res , FilterChain chain , Authentication auth) throws IOException , ServletException {
}
}
3)Controller Class:-
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.photoapp.users.dto.UserDto;
import com.photoapp.users.model.CreateUserRequestModel;
import com.photoapp.users.model.CreateUserResponseModel;
import com.photoapp.users.service.ApiUserService;
#RestController
#RequestMapping("/users")
public class UsersController {
#Autowired
private ApiUserService apiUserService;
#GetMapping("/status/check")
public String status() {
return "Working";
}
#PostMapping( consumes = {MediaType.APPLICATION_XML_VALUE , MediaType.APPLICATION_JSON_VALUE } ,
produces = {MediaType.APPLICATION_XML_VALUE , MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<CreateUserResponseModel> createUser(#RequestBody CreateUserRequestModel userDetails) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
UserDto userDto = modelMapper.map(userDetails, UserDto.class);
UserDto createdUser = apiUserService.createUser(userDto);
CreateUserResponseModel responseModel = modelMapper.map(createdUser , CreateUserResponseModel.class);
return ResponseEntity.status(HttpStatus.CREATED).body(responseModel);
}
}
4)Service Implementation Class:-
import java.util.ArrayList;
import java.util.UUID;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.photoapp.users.dao.UserRepository;
import com.photoapp.users.dto.UserDto;
import com.photoapp.users.entity.UserEntity;
#Service
public class ApiUserServiceImpl implements ApiUserService{
UserRepository userRepository;
BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public ApiUserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
public UserDto createUser(UserDto userDetails) {
userDetails.setUserId(UUID.randomUUID().toString());
userDetails.setEncryptedPassword(bCryptPasswordEncoder.encode(userDetails.getPassword()));
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
UserEntity userEntity = modelMapper.map(userDetails, UserEntity.class);
userRepository.save(userEntity);
return userDetails;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
UserEntity userEntity = userRepository.findByEmail(username);
if(userEntity == null) throw new UsernameNotFoundException(username);
return new User(userEntity.getEmail() , userEntity.getEncryptedPassword() , true ,true ,true ,true , new ArrayList<>());
}
}
Can you check if gateway.ip property is set in your environment variavales? if its mac,try echo ${gateway.ip}.
Other point is why are you limiting to just one ip? any specific reason?
Also can you confirm if removing .hasIpAddress(environment.getProperty("gateway.ip")) works?
In my application i configure AuthenticationEntryPoint in spring boot security config because if any error occurred (even my custom 404 - item was not found) spring boot catched error and return 401 (in my case) but i remeber that some people told about 403. So i think that 403 could hide real error from you
Therefore you could try to catch error via configure exception handling
.exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().sameOrigin();
http.cors();
http.csrf().disable()
.authorizeRequests().antMatchers("/", "/callback", "/login**", "/webjars/**", "/error**").permitAll()
.and()
.authorizeRequests().antMatchers("/api/**").authenticated()
.and()
.authorizeRequests().antMatchers("/h2-console/**").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui.html").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui/**").permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
.and()
.logout().permitAll().logoutSuccessUrl("/");
}
#Bean
public PrincipalExtractor getPrincipalExtractor(){
return new KeyCloakUserInfoExtractorService();
}
#Autowired
private ResourceServerTokenServices resourceServerTokenServices;
}
My AuthenticationEntryPoint looks like:
#ControllerAdvice
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException auth) throws IOException, ServletException {
// 401
setResponseError(response, HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
#ExceptionHandler (value = {AccessDeniedException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
// 403
setResponseError(response, HttpServletResponse.SC_FORBIDDEN, String.format("Access Denies: %s", accessDeniedException.getMessage()));
}
#ExceptionHandler (value = {NotFoundException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, NotFoundException notFoundException) throws IOException {
// 404
setResponseError(response, HttpServletResponse.SC_NOT_FOUND, String.format("Not found: %s", notFoundException.getMessage()));
}
#ExceptionHandler (value = {Exception.class})
public void commence(HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException {
//logger.error(String.format("An error occurred during request: %s %s error message: %s",
//request.getMethod(), request.getRequestURL(), exception.getMessage()));
// 500
setResponseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format("Internal Server Error: %s", exception.getMessage()));
}
private void setResponseError(HttpServletResponse response, int errorCode, String errorMessage) throws IOException{
response.setStatus(errorCode);
response.getWriter().write(errorMessage);
response.getWriter().flush();
response.getWriter().close();
}
//private final Logger logger = LoggerFactory.getLogger(this.getClass());
}
Hope it helps you to understand reason of 403
You must permit all of the swagger related URLs.
http.authorizeRequests().antMatchers(
"/swagger-ui.html/**",
"/webjars/springfox-swagger-ui/**",
"/swagger-resources/**",
"/swagger-ui/**",
"/v2/api-docs/**"
).permitAll();

How to make spring-boot 1.5.0 support this oauth2 solution?

I am using spring boot 1.5.0 with java 7 and am using following classes for implementation of Oauth for securing REST API
1) AuthorizationServerConfiguration.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM="MY_OAUTH_REALM";
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
int accessTokenMinutesValidity = 60;
int refreshTokenMinutesValidity = 24 * 60;
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "client_credentials", "refresh_token")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("{noop}secret")
.accessTokenValiditySeconds(60 * accessTokenMinutesValidity ).//Access token is only valid for 60 minutes.
refreshTokenValiditySeconds(60 * refreshTokenMinutesValidity);//Refresh token is only valid for 24 hours
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.allowFormAuthenticationForClients()
.realm(REALM+"/client");
}
}
2) MethodSecurityConfig.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private OAuth2SecurityConfiguration securityConfig;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
3) OAuth2SecurityConfiguration.java
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
#SuppressWarnings("deprecation")
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private DataSource dataSource;
#Primary
#Bean
public DataSource customDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//dataSource properties set here
return dataSource;
}
#Bean
#ConfigurationProperties("spring.datasource")
public DataSource ds() {
return customDataSource();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder encoder = NoOpPasswordEncoder.getInstance();
BCryptPasswordEncoder enc;
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select USERNAME, ENC_PASSWD as PASSWORD, IS_ACTIVE AS ENABLED FROM USER_MSTR WHERE USERNAME=?")
.authoritiesByUsernameQuery("select USERNAME, 'ROLE_CLIENT' as ROLE from USER_MSTR where USERNAME=?")
.passwordEncoder(NoOpPasswordEncoder.getInstance())
;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").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;
}
}
4) ResourceServerConfiguration.java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
#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.
anonymous().disable()
.requestMatchers()
.antMatchers("/category_mstr/**", "/equipment/**", "/param_mstr/**", "/chklist_txn/**", "/settings/**", "/user/**")
.and().authorizeRequests()
.antMatchers("/category_mstr/**", "/equipment/**", "/param_mstr/**", "/chklist_txn/**", "/settings/**", "/user/**")
.access("hasRole('ROLE_CLIENT')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
However when accessing /oauth/token , I am getting error message saying "bad credentials" even when provided proper credentials
However if I use java 8 and spring boot 2.1.5 for same issue, then it runs fine and works as expected.
The old versions it was not necessary to add {noop} then remove it, since the latest versions work by using the password encoder {noop}.

"could not obtain access token" error in spring security oauth2

In my spring project i have an oauth2 client app running on localhost:9999 and an oauth2 authorization server running on localhost:8080.
In result, after approval page, i see error page that i don't know what is the problem?
when press F12 i see that set-cookie done! but /oauth/token not called! and /me also not called! and browser not redirect to localhost:9999.
my client app
package sso.client;
import java.security.Principal;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#EnableAutoConfiguration
#Configuration
#EnableOAuth2Sso
#RestController
public class App {
#RequestMapping("/")
public String home(Principal user) {
return "Hello " + user.getName();
}
public static void main(String[] args) {
new SpringApplicationBuilder(App.class)
.properties("spring.config.name=client").run(args);
}
}
client.yml
server:
port: 9999
security:
oauth2:
client:
client-id: acme
client-secret: acmesecret
access-token-uri: http://localhost:8080/oauth/token
user-authorization-uri: http://localhost:8080/oauth/authorize
resource:
user-info-uri: http://localhost:8080/me
my authorization server
package sso.raymon;
import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#Configuration
#EnableAutoConfiguration
#RestController
#EnableWebSecurity
public class App extends WebSecurityConfigurerAdapter
{
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter{
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
// TODO Auto-generated method stub
security.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("acme")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "write")
.secret("acmesecret");
// #formatter:on
}
}
#RequestMapping("/me")
public String home(Principal user) {
return user.getName();
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/me")
.authorizeRequests().anyRequest().authenticated();
}
}
}
application.properties
security.user.name=forough
security.user.password=m123
Error in blow URL:
localhost:9999/login?code=xZgYwZ&state=27XzVY
Error:
Whitelable Error Page
this application has no explicit mapping for /error, so you are seeing this a fallback.
There was an unexpected erroe (type=Unauthorized, atatus=401).
Authentication Failed: could not obtain access token

How to persist oauth access tokens in spring security JDBC?

When the user login I generate a token so when the user want to access information of RESTapi he will not login again , the code works but I have a problem.
The token that spring generate works but when I recompile the code I wrote, this token doesn't work anymore and I should request a new token to use the "bearer $Token " is this a normal behavior or I should fix something in the settings?
Example :
curl -u test: http://localhost:8080/oauth/token -d "grant_type=password&username=id&password=pass"
{"access_token":"a46c3cf4-6777-4a61-9f71-268be047c383","token_type":"bearer","refresh_token":"8ef69c18-1a9e-47c0-ba80-b51a34144e9a","expires_in":603005,"scope":"read write trust"}
When I recompile the code :
curl -u test: http://localhost:8080/oauth/token -d "grant_type=password&username=id&password=pass"
{"access_token":"1a69f140-47ac-4dde-9786-1d4f14f9a389","token_type":"bearer","refresh_token":"be99d434-54a0-4273-add8-cccad95caec3","expires_in":604799,"scope":"read write trust"}
this is my code :
import com.lms.entities.UsersEntity;
import com.lms.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
#Configuration
#ComponentScan
#EnableAutoConfiguration
#RestController
class SpringBackendScoutApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBackendScoutApplication.class, args);
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.requestMatchers()
.antMatchers(
Constants.USERS, Constants.COMMENTS, Constants.FRIENDS_REQUEST,
Constants.MUSICS, Constants.PHOTOS, Constants.POSTS, Constants.VIDEOS,
Constants.PROFILE_PHOTOS, Constants.FRIENDS, "/"
).and()
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')")
.and().logout();
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("sparklr");
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("sparklr")
.accessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(7))
;
}
}
#Configuration
#EnableWebSecurity
protected static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserRepository userRepository;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
for (UsersEntity user : userRepository.findAll())
if (user.getUsername() != null && user.getPassword() != null)
auth.inMemoryAuthentication().withUser(user.getUsername()).password(user.getPassword()).roles("USER");
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
}
}
To solve the problem , you should add a token store to persist the token generated each time :
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Value("${spring.datasource.driverClassName}")
private String oauthClass;
#Value("${spring.datasource.url}")
private String oauthUrl;
#Bean
public TokenStore tokenStore() {
DataSource tokenDataSource = DataSourceBuilder.create()
.driverClassName(oauthClass)
.username("root")
.password("")
.url(oauthUrl)
.build();
return new JdbcTokenStore(tokenDataSource);
}
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("scout").tokenStore(tokenStore());
}
For the other class :
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.tokenStore(tokenStore());
}
}
In applications.properties it should be configured :
spring.datasource.url=jdbc:mysql://localhost:3306/MyDatabase
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
and Finally you should add the two tables in your database :
create table oauth_access_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication BLOB,
refresh_token VARCHAR(256)
);
create table oauth_refresh_token (
token_id VARCHAR(256),
token BLOB,
authentication BLOB
);
By default, an in memory token store is configured. If you want to persist the tokens between restarts then you need to configure a persistent token store (JdbcTokenStore for example)
Edit to add steps:
This SQL will create the basic schema that you need.
Create a DataSource bean connected to the database containing that schema and then add the following bean to your OAuth config
#Autowired
DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}

Resources