Here is my app setup with Spring Boot 1.2.3.RELEASE
Authentication App
#SpringBootApplication
#RestController
#SessionAttributes("authorizationRequest")
#EnableResourceServer
public class AuthserverApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthserverApplication.class, args);
}
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
#Configuration
#Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.formLogin().loginPage("/login").permitAll()
.and().requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and().authorizeRequests()
.anyRequest().authenticated();
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
}
}
#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("acme").secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("openid")
.and().withClient("trusted").secret("shuush").authorizedGrantTypes("client_credentials").scopes("openid");
}
}
}
with application.properties
server.port=9999
security.user.password=password
server.contextPath=/uaa
Resource Application
#SpringBootApplication
#EnableOAuth2Resource
#RestController
public class ResourceApplication {
#RequestMapping("/resource")
public Map<String, String> resource() {
Map<String, String> t = new HashMap<String, String>();
t.put("Hello", "World!");
return t;
}
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class, args);
}
}
with application.properties
server.port: 8801
spring.oauth2.resource.userInfoUri: http://localhost:9999/uaa/user
Client Side:
Step 1 : I use curl to get bearer token
curl -v -u trusted:shuush -d "grant_type=client_credentials" http://localhost:9999/uaa/oauth/token
to get a response
{"access_token":"a9983ae3-e4db-4702-8039-7ecfd7472594","token_type":"bearer","expires_in":43095,"scope":"openid"}
Step 2 : I access resource
curl -v -H "Authorization: Bearer a9983ae3-e4db-4702-8039-7ecfd7472594" http://localhost:8801/resource
which gets me a dandy response
{"Hello":"World!"}
Step 3 : Long after my token as expired I try to access resource again (same as step 2) and I was expecting a 401 http code with response something like
{"error":"unauthorized","error_description":"token expired....."}
however I get 500 code with response
{
"timestamp": 1433861559805,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.security.oauth2.client.resource.UserRedirectRequiredException",
"message": "A redirect is required to get the users approval",
"path": "/resource"
}
Stack trace:
org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.getRedirectForAuthorization(AuthorizationCodeAccessTokenProvider.java:347)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:194)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:565)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:143)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:262)
at org.springframework.cloud.security.oauth2.resource.UserInfoTokenServices.getMap(UserInfoTokenServices.java:105)
at org.springframework.cloud.security.oauth2.resource.UserInfoTokenServices.loadAuthentication(UserInfoTokenServices.java:58)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Wondering if I am missing some configuration. Any pointers will be appreciated?
Update 1:
Oddly sometimes I get different response
{"error":"invalid_request","error_description":"Possible CSRF detected - state parameter was present but no state could be found"}
Little debugging shows the endpoint http://localhost:9999/uaa/user does throw error like {error="invalid_token", error_description="Invalid access token..."} but is swallowed to throw org.springframework.security.oauth2.client.http.AccessTokenRequiredException at https://github.com/spring-projects/spring-security-oauth/blob/2.0.7.RELEASE/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorHandler.java#L155
Further more org.springframework.security.oauth2.client.OAuth2RestTemplate swallows org.springframework.security.oauth2.client.http.AccessTokenRequiredException and retries bad token by removing the token at https://github.com/spring-projects/spring-security-oauth/blob/2.0.7.RELEASE/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/OAuth2RestTemplate.java#L143 throws entirely different error.
I believe this is a bug unless something is wrong with my config
Related
I am under Spring Boot 2.0.5 (Spring Security 5.0.8)
I have setup a preauthentication filter chain, which works fine, only thing problematic is the exception handling, especially those thrown form my custom UserDetailsService. I also tried to return a User with "enabled = false" but still the same.
See code below for my custom UserDetailsService:
#Component
#Slf4j
public class PreAuthenticatedUserDetailsService
implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Autowired
private AccessService accessService;
#Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws AuthenticationException {
String accessId = token.getCredentials() + "";
try {
AccessEntity accessEntity = accessService.getAccessByAccessId(accessId);
if (accessEntity == null) {
throw new BadCredentialsException(String.format("Unknown access (AccessId=%s)", accessId));
}
List<AccessRole> accessRoles = accessEntity.getRoles();
if (!accessEntity.getActive()) {
return new User(accessEntity.getUid(), accessEntity.getUid(), false, true, true, true, accessRoles.stream()
.map(role -> new SimpleGrantedAuthority(role.getValue())).collect(Collectors.toList()));
//throw new AccessDeniedException(String.format("Inactive access (AccessId=%s)", accessId));
}
return new User(accessEntity.getUid(), accessEntity.getUid(), accessRoles.stream()
.map(role -> new SimpleGrantedAuthority(role.getValue())).collect(Collectors.toList()));
} catch (Exception e) {
log.error("Exception in load user details", e);
throw new BadCredentialsException(String.format("Authentication issue for accessId=%s", accessId));
}
}
}
And my Security Configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String SPRING_ROLE_ANONYMOUS = "ANONYMOUS"; // ("ROLE_" is automatically inserted as prefix)
#Autowired
private AppConfigProperties appConfigProperties;
#Autowired
RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
RESTAccessDeniedHandler accessDeniedHandler;
#Autowired
PreAuthenticatedUserDetailsService preAuthenticatedUserDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preauthAuthProvider());
}
#Bean
public PreAuthenticatedAuthenticationProvider preauthAuthProvider() {
PreAuthenticatedAuthenticationProvider preauthAuthProvider = new PreAuthenticatedAuthenticationProvider();
preauthAuthProvider.setPreAuthenticatedUserDetailsService(preAuthenticatedUserDetailsService);
return preauthAuthProvider;
}
#Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setPrincipalRequestHeader(appConfigProperties.getSecurity().getRequestHeaderTokenKeyName());
filter.setCredentialsRequestHeader(appConfigProperties.getSecurity().getRequestHeaderTokenKeyName());
filter.setExceptionIfHeaderMissing(false);
filter.setAuthenticationManager(authenticationManager());
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher protected_urls = new OrRequestMatcher(new AntPathRequestMatcher(
appConfigProperties.getSecurity().getProtectedUrls()));
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(requestHeaderAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.POST , appConfigProperties.getSecurity().getUserAuthenticatePath())
.hasRole(SPRING_ROLE_ANONYMOUS)
.requestMatchers(protected_urls).authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable(); // disable autoconfig
}
/**
* Disable Spring boot automatic filter registration.
*/
#Bean
FilterRegistrationBean<RequestHeaderAuthenticationFilter> disableAutoRegistration(final RequestHeaderAuthenticationFilter filter) {
final FilterRegistrationBean<RequestHeaderAuthenticationFilter> registration = new FilterRegistrationBean<>(filter);
registration.setEnabled(false);
return registration;
}
}
#Component
#Slf4j
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Autowired
private AppConfigProperties appConfigProperties;
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
log.error("********* Authentication Entry Point exception", authException);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
String responseContentJson = null;
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
responseContentJson = new Gson().toJson(GlobalExceptionHandler.createSWError(SWErrorCode.HTTP,
String.format("Failed authentication: missing request header '%s' or invalid value",
appConfigProperties.getSecurity().getRequestHeaderTokenKeyName())));
PrintWriter out = response.getWriter();
out.print(responseContentJson);
out.flush();
response.flushBuffer();
}
}
I also tried to use an "accessDeniedHandler" but it never goes through it.
The issue is that for any exception thrown (either in my custom UserDetailsService or others), it goes through my custom "AuthenticationEntryPoint" with an always similar exception, see below:
org.springframework.security.authentication.InsufficientAuthenticationException: Full authentication is required to access this resource
at org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:190)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:141)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:121)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:155)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:123)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Any idea would be great!
Hello everyone,
I'm practicing OAuth2 in Spring boot.I have developed the application when i access get resources, i'm getting the response but for post resources i have to provide username and password which i'm passing in request but it still gives me this response
curl -i --user admin:admin -H Accept:application/json -X PUT http://localhost:8080/api/user/addUpdateUser -H Content-Type: application/json -d '{ "userId": 3, "firstName": "M.Danish", "lastName": "Khan", "userName": "danishkhan", "address": "Mardan", "phone": "04543545435" }'
{
"timestamp": 1464778621656,
"status": 401,
"error": "Unauthorized",
"message": "Access Denied",
"path": "/api/user/addUpdateUser"
}
This my code.
Web Security Configuration
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET).permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().csrf().disable();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
OAuth Resource Server Configuration
#Configuration
#EnableResourceServer
public class OAuth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private final String RESOURCE_ID="SpringOAuth";
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
/*#Autowired
private UserDetailsService userDetailsService;*/
#Override
public void configure(HttpSecurity http) throws Exception {
http .exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET).permitAll()
.anyRequest().authenticated()
/*.and().userDetailsService(userDetailsService); was just checking whether it will work with this or not*/
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
}
OAuth Authorization Server Configuration
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final String RESOURCE_ID="SpringOAuth";
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
private UserDetailsService userDetailsService;
#Autowired
AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.authorizedGrantTypes("password", "refresh_token")
.authorities("ROLE_USER")
.scopes("read")
.resourceIds(RESOURCE_ID)
.secret("secret").accessTokenValiditySeconds(3600);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
Controller
#Controller
#RequestMapping("/api/user")
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(value = "/addUpdateUser",method = RequestMethod.POST)
public ResponseEntity<Void> add_UpdateUser(#RequestBody User user){
if(user==null){
return new ResponseEntity<Void>(HttpStatus.EXPECTATION_FAILED);
}else{
userService.add_UpdateUser(user);
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
}
#RequestMapping("/getAllUser")
public ResponseEntity<List<User>> getAllUsers(){
return new ResponseEntity<List<User>>(userService.getAllUsers(),HttpStatus.OK);
}
#RequestMapping(value = "/deleteUser",method = RequestMethod.POST)
public ResponseEntity<Void> deleteUser(#RequestBody String userName){
if(userName.equals("")){
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}else {
userService.deleteUser(userName);
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
}
Your content type header must be surround by quotation marks because you have a space in it.
-H Content-Type: application/json
Should be
-H "Content-Type: application/json"
Otherwise the shell considers them as separate arguments. Like this
$ curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer 27f9e2b7-4441-4c03-acdb-7e7dc358f783" -d '{"apiKey": "key", "tag": "tag"}' localhost:8080/isTagAvailable
Also you didn't get an access token first.
I am trying to configure separate auth and resource servers for oauth2.
I am able to configure authrization server successfully and able to authenticate and generate access tokens. Now I want to configure a resource server which can talk to auth server with api end point to validate the access tokens.
Below is my resource server configuration.
#Configuration
#EnableResourceServer
#EnableWebSecurity
public class Oauth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Oauth2SecurityConfiguration before");
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/v1/**").authenticated();
System.out.println("Oauth2SecurityConfiguration after");
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9000/authserver/oauth/check_token");
remoteTokenServices.setClientId("clientId");
remoteTokenServices.setClientSecret("clientSecret");
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
#Override
#Bean
public AuthenticationManager authenticationManager() throws Exception {
OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
authenticationManager.setTokenServices(remoteTokenServices());
return authenticationManager;
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
System.out.println("http.csrf().disable()");
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api/v1/**").fullyAuthenticated();
System.out.println("http.authorizeRequests().anyRequest().authenticated()");
}
}
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
Question :
1. why I AuthenticationManager at resource server while all the authentication is delegated to auth server. (I had to add it to load application context)
Apart from this I am facing below issues.
Even though I am not passing authorization headers and access token with the request. It's going through.
http GET "http://localhost:8080/DataPlatform/api/v1/123sw/members"
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Mon, 19 Oct 2015 19:45:14 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"entities": [],
"errors": [],
"message": null
}
The filters are only invoked at once I don't see the logs for following requests. Does it cache authorization somewhere?
I am new to spring oauth Please let me know if I am doing anything wrong. I am using
spring-security-oauth2 : 2.0.7.RELEASE
spring-security-core : 4.0.1.RELEASE
java : 1.8
The main point make separate endpoints for auth-server and resource-server, that they can services them separately, each his own.
As show bellow "/user/getEmployeesListRole/**" -access through auth-server, "/user/getEmployeesListOAuth2/**" - access through resource-server by token, which generated by aouth2-server.Also please pay attention that auth-server and oauth2-server has the same auth-manager
Configuration of spring-boot aouth2-server, resource-server, auth-server in one spring-boot app
1.Entry Point:/*AuthApplication.java*/
#SpringBootApplication
#EnableDiscoveryClient
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}}
2. Config of aouth2-server:/*OAuth2AuthorizationConfig.java*/
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
#Qualifier("userDetailsServiceBean")
private UserDetailsService userDetailsService;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("browser")
.authorizedGrantTypes("password", "refresh_token")
.scopes("ui", "read:ui", "write:ui");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}}
2.1 aouth2-server auth-request [post with basic auth]:http://localhost:5000/uaa/oauth/token?grant_type=password&scope=ui write:ui&username=user&password=123456&client_id=browser3.Config resource-server:
/*ResourceServer.java*/
#Configuration
#EnableResourceServer
class ResourceServer extends ResourceServerConfigurerAdapter {
//Here we specify to allow the request to the
// url /user/getEmployeesList with valid access token and scope read
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/user/getEmployeesList/**")
.antMatchers("/user/getEmployeesListOAuth2/**")
.and().authorizeRequests().anyRequest().access("#oauth2.hasScope('ui')");
}}
4. Config auth-server:
/*WebSecurityConfig.java*/
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/getEmployeesListRole/**")
.access("hasAuthority('WRITE_DATA') && hasAuthority('READ_DATA')")
.anyRequest().permitAll()
.and().formLogin().permitAll()
.and().logout().permitAll()
.and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin")
.password("admin")
.authorities("WRITE_DATA", "READ_DATA");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
You don't need #EnableWebSecurity on Oauth2SecurityConfiguration #EnableResourceServer is enough.
You should also replace extends WebSecurityConfigurerAdapter with extends ResourceServerConfigurerAdapter.
If you want to use your RemoteTokenServices instance I recommend you override ResourceServerConfigurerAdapter public void configure(ResourceServerSecurityConfigurer resources) throws Exception with
#Override
public void configure( ResourceServerSecurityConfigurer resources ) throws Exception
{
resources.tokenServices( serverConfig.getTokenServices() );
}
I am trying spring security and spring oauth2 in my project and have separated my authorization server and resource server. I didn't want to share a token store between these two servers so I decided to use RemoteTokenServices and the check_token endpoint. Everything was fine except when I used an access token to query the resource server, I got "401 Unauthorized" error as follows:
2015-10-19 11:50:10.291 DEBUG 2590 --- [nio-8080-exec-1] o.s.web.client.RestTemplate : POST request for "http://localhost:9080/uaa/oauth/check_token/" resulted in 401 (Unauthorized); invoking error handler
2015-10-19 11:50:10.293 DEBUG 2590 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2015-10-19 11:50:10.293 DEBUG 2590 --- [nio-8080-exec-1] o.s.web.filter.RequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade#41f4867a
2015-10-19 11:50:10.297 ERROR 2590 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[jerseyServlet] : Servlet.service() for servlet [jerseyServlet] in context with path [] threw exception
org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
The code for the Authorisation server:
#Configuration
#EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Bean
public DefaultAccessTokenConverter defaultAccessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(this.tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(defaultAccessTokenConverter());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
And the security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication();
// .withUser("John").roles("ADMIN").password("password")
// .and()
// .withUser("Mary").roles("BASIC").password("password");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated()
.and().httpBasic().realmName("OAuth Server");
http.csrf().disable();
}
}
The Resource Server is set up as follows:
#Configuration
#EnableResourceServer
public class ResourceConfiguration extends ResourceServerConfigurerAdapter {
private static String RESOURCE_ID = "xn-resource-id";
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Bean
public RemoteTokenServices remoteTokenServices(final #Value("${auth.server.url}") String checkTokenUrl,
final #Value("${auth.server.client_id}") String clientId,
final #Value("${auth.server.client_secret}") String clientSecret) {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl);
remoteTokenServices.setClientId(clientId);
remoteTokenServices.setClientSecret(clientSecret);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
}
I tested the security settings with curl and used client_credentials grant type.
Does anyone help me figure out what's the issue with the above code?
Looks like you are using incorrect url. Try to repleace it with:
http://localhost:9080/uaa/oauth/check_token
(notice that url is not ended with /)
I am new to Spring and I would like to protect my API with Spring Security. But even after two days of reading tutorials I can't get it working.
If I do a POST to j_spring_security_check I get the following response:
{
"timestamp": 1407420906360,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.NullPointerException",
"message": null,
"path": "/j_spring_security_check"
}
caused by the following Java-Exception:
java.lang.NullPointerException: null
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:683)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1720)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1679)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Line 94 in UsernamePasswordAuthenticationFilter is the following. The exception is thrown because getAuthenticationManager() returns null.
return this.getAuthenticationManager().authenticate(authRequest);
I REALLY don't get why this method returns null.
This is my security config:
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AuthenticationProvider authentificationProvider;
#Autowired
AuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
AuthenticationSuccessHandler successHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authentificationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint).and()
.addFilter(getSuccessFilter()).anonymous()
.authorities(UserRightsProvider.getAnonymousAuthorities())
.and().authorizeRequests().anyRequest().authenticated();
}
protected Filter getSuccessFilter() {
UsernamePasswordAuthenticationFilter authenticationFilter = new UsernamePasswordAuthenticationFilter();
authenticationFilter.setAuthenticationSuccessHandler(successHandler);
return authenticationFilter;
}
}
The application is started the following way:
#Order(1)
public class WebAppSecurityInitializer extends AbstractSecurityWebApplicationInitializer {}
and
#Configuration
#EnableAutoConfiguration
#ComponentScan({ "my.packet" })
#PropertySource("classpath:myProperties.properties")
#Order(2)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
application.showBanner(false);
application.sources(getClass());
return application;
}
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setShowBanner(false);
app.run(args);
}
}
Any help is appreciated! :-)
in getSuccessFilter you need to have
filter.setAuthenticationManager(this.authenticationManagerBean());
You aren't using spring autowiring/DI to make that thing so you have to do everything by hand. Spring Security Filters (generally) need a handle to auth manager