Jersey - Return customised error instead of stack trace when url has special characters - spring-boot

My Http Request Url has some special character like -
../MyOrder(Id='1234')?{#sleep(15)}
I am using following tech stack -
Jersey + Spring Boot and tomcat as server
Now I want this url to return some customized error instead of returning stack trace, I have tried Exception Mapper , here is the code
#Provider
public class JsonProcessingExceptionMapper implements ExceptionMapper{
public static class Error {
public String key;
public String message;
}
#Override
public Response toResponse(IllegalArgumentException exception) {
Error error = new Error();
error.key = "bad-json";
error.message = exception.getMessage();
return Response.status(Status.BAD_REQUEST).entity(error).build();
}
}
but in with it , or without this , I am getting following stack trace -
Apache Tomcat/7.0.54 - Error report HTTP Status 500 - Illegal character "#" at position 1 is not allowed as a start of a name in a path template "{#sleep(15)}$format=json".type Exception reportmessage Illegal character "#" at position 1 is not allowed as a start of a name in a path template "{#sleep(15)}$format=json".description The server encountered an internal error that prevented it from fulfilling this request.exception <pre>java.lang.IllegalArgumentException: Illegal character "#" at position 1 is not allowed as a start of a name in a path template "{#sleep(15)}$format=json".
org.glassfish.jersey.uri.internal.UriTemplateParser.parseName(UriTemplateParser.java:305)
org.glassfish.jersey.uri.internal.UriTemplateParser.parse(UriTemplateParser.java:235)
org.glassfish.jersey.uri.internal.UriTemplateParser.<init>(UriTemplateParser.java:107)
org.glassfish.jersey.uri.UriTemplate.createURIComponent(UriTemplate.java:891)
org.glassfish.jersey.uri.UriTemplate.createURIWithStringValues(UriTemplate.java:858)
org.glassfish.jersey.uri.UriTemplate.createURIWithStringValues(UriTemplate.java:797)
org.glassfish.jersey.uri.UriTemplate.createURI(UriTemplate.java:762)
org.glassfish.jersey.uri.internal.JerseyUriBuilder._build(JerseyUriBuilder.java:893)
org.glassfish.jersey.uri.internal.JerseyUriBuilder.build(JerseyUriBuilder.java:810)
org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:330)
org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:219)
org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextFilterConfiguration$1.doFilterInternal(EndpointWebMvcAutoConfiguration.java:257)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:186)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
note The full stack trace of the root cause is available in the Apache Tomca

Related

Serving static content results in Request method 'GET' not supported

I want to serve static content from my static directory in my project resources, but I get error: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported.
I have added configured Spring Security to ignore requests starting with "/static/". I have added resource handler to WebMvcConfig to look for the static content in various different locations, I have tried removing the resource handler altogether.
I have tried adding permitAll() to the "/static/" path.
I have googled the error message and various combinations of it to find solutions. This is where I came up with the Spring Security and WebMvcConfigs. Just that these did not solve the problem for me.
Finally I have tried placing static files into different locations. The directory structure is as follows:
\resources\static\hello.css
\resources\static\css\hello.css
\resources\templates\home.html
\resources\templates\error.html
Thymeleaf works correctly, taking the templates from the templates directory.
The WebMvcConfig:
// Config Thymeleaf
private static final String VIEWS = "classpath:templates/";
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
private ISpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
return engine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix(VIEWS);
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
// Enable static resources
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("/resources/static/", "classpath:static/", "/static/", "classpath:resources/static");
}
The Spring Security config:
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**");
}
I expect to see my css file, but instead I see the error page generated by exception handler with the message "Request method 'GET' not supported"
Here is the debug trace from the point I request the css file:
2019-06-07 11:30:29.694 DEBUG 5398 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/static/css/hello.css'; against '/static/**'
2019-06-07 11:30:29.694 DEBUG 5398 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /static/css/hello.css has an empty filter list
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:200)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:419)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:365)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:65)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:401)
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1232)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1015)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:209)
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.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:200)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)
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)
After hours of debugging I found the reason static content was not served!
It was because of a controller with mapping without the path:
#PostMapping()
After adding a path to the mapping, the configuration above works as it should.
Very weird that it messed up the serving of static files!
Big thanks to #Ganesh for his tip in Spring Boot not serving static content
thanks #Avaruuskadetti you saved hours of my time
DEBUG:
package org.springframework.web.servlet >>>> HandlerExecutionChain
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping hm = (HandlerMapping)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

Spring-Boot and DialogFlow : Why I receive an anonymous request on my webhook defined in DialogFlow?

I'm creating a web application with Spring Boot that defines a REST API accessible via OAuth2 authentication for use with Google Assistant
I configured DialogFlow (Webhook fulfillment configured with the URL to the endpoint of my REST API)
I configured Actions on Google: I configured the Account Linking section with OAuth information (client ID, client Secret, Authorization URL, Token URL, Scopes ...)
I tested my application with my smartphone via the Google Home application.
It tells me: "Before I can use "My App", I need to associate your "My App" account with Google. Do you agree with that?"
I say, "Yes."
I then have access to my web application for OAuth authentication.
I validate, and it says: "Perfect! Your "My App" account is now connected to Google"
Then I write the sentence "Turn on my TV", it then calls the fulfillment webhook that calls my REST API.
Only I'm getting a request that doesn't seem right. I have an error indicating that the user is anonymous. It is as if the access-token had not been transmitted in the'Authorization' header.
I can't find a way to get the complete request (Header + Body) that is sent.
I also tested on the Actions on Google Simulator but I only see the request body, not the headers. I looked at Google's logs but I don't have any more details.
Here are the logs :
19:47:41.263 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /api/fulfillment' doesn't match 'GET /**
19:47:41.264 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request '/api/fulfillment' matched by universal pattern '/**'
19:47:41.264 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /api/fulfillment; Attributes: [#oauth2.throwOnError(#oauth2.hasScope('write'))]
19:47:41.264 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#2629f42a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#ffffc434: RemoteIpAddress: 35.184.134.60; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
19:47:41.268 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Insufficient scope for this resource
at org.springframework.security.oauth2.provider.expression.OAuth2SecurityExpressionMethods.throwOnError(OAuth2SecurityExpressionMethods.java:72)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:120)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:111)
at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:391)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:89)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:116)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:306)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:26)
at org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:52)
at org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:33)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:63)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
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.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.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
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.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:90)
at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:77)
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.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:613)
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:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
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)
Caused by: org.springframework.security.oauth2.common.exceptions.InsufficientScopeException: Insufficient scope for this resource
at org.springframework.security.oauth2.provider.expression.OAuth2SecurityExpressionMethods.throwOnError(OAuth2SecurityExpressionMethods.java:71)
... 81 common frames omitted
19:47:41.271 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Calling Authentication entry point.
19:47:41.275 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.o.p.e.DefaultOAuth2ExceptionRenderer - Written [error="unauthorized", error_description="Full authentication is required to access this resource"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#1ff3a97]
19:47:41.276 [https-jsse-nio-9443-exec-6] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
I tested on "OAuth 2.0 Playground" (https://developers.google.com/oauthplayground), and it works perfectly!
Is this due to the fact that my App hasn't been released and I'm still in test mode?
Anybody got any ideas?
This is because Google isn't sending the bearer token in the Authorization header (for various reasons, but at least partially because some services are using this to authorize the service - not the user of the service). It sends it as part of the JSON body.
If you are using the Action SDK, you'll find this in user.accessToken. In Dialogflow, this will be under originalDetectIntentRequest.payload.user.accessToken.
Following Prisoner's answer, I created a custom org.springframework.security.oauth2.provider.authentication.TokenExtractor to manage the access-token present in the request body.
Here is the Kotlin code:
class BodyTokenExtractor : BearerTokenExtractor() {
private val logger = LogFactory.getLog(BodyTokenExtractor::class.java)
override fun extractToken(request: HttpServletRequest): String? {
var token: String? = null
if (HttpMethod.POST.matches(request.method)) {
token = extractBodyToken(request)
}
if (token == null) {
logger.debug("Token not found in body. Trying request headers.")
token = super.extractToken(request)
} else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE)
}
return token
}
/**
* Extract the OAuth token from the request body.
*
* #param request The request.
* #return The token, or null if no OAuth authorization header was supplied.
*/
protected fun extractBodyToken(request: HttpServletRequest): String? {
try {
val requestBody = IOUtils.toString(request.reader)
val request = JacksonFactory().fromString(requestBody, Map::class.java)
val originalDetectIntentRequest = request["originalDetectIntentRequest"] as Map<String, Object>
if (originalDetectIntentRequest != null) {
val payload = originalDetectIntentRequest["payload"] as Map<String, Object>
if (payload != null) {
val user = payload["user"] as Map<String, Object>
if (user != null) {
return user["accessToken"] as String?
}
}
}
} catch (e: IOException) {
logger.debug("An error occurred while reading the request body: " + e.message, e)
}
return null
}
}
This class is then called in the Resource Server :
#Configuration
#EnableResourceServer
class OAuthResourceServerConfig() : ResourceServerConfigurerAdapter() {
...
#Throws(Exception::class)
override fun configure(resources: ResourceServerSecurityConfigurer) {
resources.resourceId(resourceId).tokenStore(tokenStore()).tokenExtractor(tokenExtractor())
}
private fun tokenStore(): TokenStore {
return JdbcTokenStore(dataSource)
}
private fun tokenExtractor(): TokenExtractor {
return BodyTokenExtractor()
}
...
}
It is important to define a custom filter to allow to read multiple times the request body :
#Bean
fun multiReadFilter(): FilterRegistrationBean<*> {
val registrationBean = FilterRegistrationBean<CachedRequestWrapperFilter>()
val multiReadRequestFilter = CachedRequestWrapperFilter()
registrationBean.filter = multiReadRequestFilter
registrationBean.order = SecurityProperties.DEFAULT_FILTER_ORDER - 2
registrationBean.urlPatterns = Arrays.asList("/api/*")
return registrationBean
}
class CachedRequestWrapperFilter : Filter {
#Throws(ServletException::class)
override fun init(config: FilterConfig) {
// nothing goes here
}
#Throws(java.io.IOException::class, ServletException::class)
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
val requestWrapper = CachedHttpServletRequest(request as HttpServletRequest)
// Pass request back down the filter chain
chain.doFilter(requestWrapper, response)
}
override fun destroy() {
/* Called before the Filter instance is removed from service by the web container*/
}
}
public class CachedHttpServletRequestextends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedContent;
public CachedHttpServletRequest(HttpServletRequest request) throws IOException {
// Read the request body and populate the cachedContent
}
#Override
public ServletInputStream getInputStream() throws IOException {
// Create input stream from cachedContent
// and return it
}
#Override
public BufferedReader getReader() throws IOException {
// Create a reader from cachedContent
// and return it
}
}
There are plenty of examples explaining how to wrap the request and read multiple times.
Example: http://www.myjavarecipes.com/tag/how-to-read-request-twice/
By doing that, it works perfectly!

redirect_uri using http instead of https

I'm using spring stack (Spring Boot 2.0.1.RELEASE) for creating a site that delegues user authentication/registration to Facebook via OAuth2. When I click the "login with facebook" button I get redirected to Facebook, but Spring Security OAuth2 is creating the redirect_uri parameter using http instead of https. The application uses https and I can't figure out where this "http" is coming from.
So, how can I make Spring create the redirect_uri parameter correctly?
UPDATE
Sorry for the original post. It was late and I wanted to have the question posted before going sleep :-)
Well, my application uses Spring Boot 2.0.1.RELEASE, which comes with Spring Security 2.0.1.RELEASE and Spring Security OAuth2 5.0.4.RELEASE. My application uses Facebook for registering and authenticating users. Currently I have a test environment running in AWS (Beanstalk) and using Amazon's SSL certificate.
When I first wrote the post my issue was that the redirect_uri parameter sent by my application (by SS actually) to Facebook had a http prefix, instead of https. This was causing an error in Facebook, which only accepts https redirect urls.
Reading the docs I found the spring.security.oauth2.client.registration.facebook.redirect-uri-template property, which I set to https://[my domain]/login/oauth2/code/{registrationId}. Now Facebook processes my authentication requests and posts back to my application.
However, with the previous parameter set, now the problem has changed. Now when the Facebook's callback hits my application at AWS I get the following exception (from the logs):
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.o.c.w.OAuth2LoginAuthenticationFilter - Request is to process authentication
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider
2018-04-11 10:51:23 [http-nio-5000-exec-5] DEBUG o.s.s.o.c.w.OAuth2LoginAuthenticationFilter - Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_redirect_uri_parameter]
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_redirect_uri_parameter]
at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:117)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:159)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:128)
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.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
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.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:81)
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:496)
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:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
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)
Taking a look at the sources I found that the issue seems to be in the following test in the org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider class:
if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
To check why this comparison is failing I checked the requests and responses using Chrome's developer tools. So, this is the call to Facebook:
https://www.facebook.com/v2.8/dialog/oauth?response_type=code&client_id=[REMOVED]&scope=public_profile%20email&state=[REMOVED]&redirect_uri=https://[REMOVED]/login/oauth2/code/facebook
Everything seems to be ok, the redirect_uri parameter is using https as expected and the complete redirect_uri seems correct.
And this is Facebook's callback:
https://[REMOVED]/login/oauth2/code/facebook?code=[REMOVED]
Once again, everything seems ok. However, SS is rejecting the user authentication because request and response redirect_uris are not matching.
And this is the issue. Any idea of what is going wrong here? Am I missing something?
I encountered the same error when I m setting up a Spring Boot application to authenticate users using Facebook OAuth2 implementation. Nginx (functions as reverse proxy) is configured to front the web app and also to offload the SSL cert.
Initially, I tried to customize the property: redirect-uri-template so that the redirect uri can be hard-coded with https://{domain}/login/oauth2/code/facebook (this is because Facebook only accepts HTTPS protocol for valid OAuth Redirect URI). It didnt work as I encountered the same error: OAuth2AuthenticationException: [invalid_redirect_uri_parameter]
Then, I found the proposed solution in link, which works for me. So, it is basically to set the OAuth2 Login Application with server.use-forward-headers=true and remove the customized property: redirect-uri-template.
Hope it helps :)
I faced with exact the same problem but with Google.
Having the following architecture of microservices
Google Auth Server
Zuul Gateway (:8080)
/ \
/ \
/ \
Other OAuth2Client (:5000)
while running at local machine everything works fine, but in AWS Elastic Beanstalk I catch the very same exception.
After debugging, I found out that in my case, when OAuth2Client is behind Zuul proxy (they implemented in separate microservices) I really get different redirect_uri values in the check inside OAuth2LoginAuthenticationProvider:
if (!authorizationResponse.getRedirectUri().equals(authorizationRequest.getRedirectUri())) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
So in my case in AWS I have following values:
authorizationResponse.getRedirectUri()
http://[INNER_AWS_ESB_IP]:5000/auth/login/oauth2/code/google
authorizationRequest.getRedirectUri()
https://[MY_PROJECT_DOMAIN_NAME]/auth/login/oauth2/code/google
where [INNER_AWS_ESB_IP] is an IP address of inner network in AWS Elastic Beanstalk and [MY_PROJECT_DOMAIN_NAME] is a domain name of my project, which is hardcoded in application.yml as redirect-uri-template parameter.
I have the following config in application.yml of my OAuth2Client microservice
server:
port: 5000
servlet:
contextPath: /auth
use-forward-headers: true
spring:
security:
oauth2:
resource:
filter-order: 3
client:
registration:
google:
client-id: [REMOVED]
client-secret: [REMOVED]
redirect-uri-template: ${MY_PROJECT_DOMAIN_NAME:http://localhost:8080}/auth/login/oauth2/code/google
scope: profile,email
Loreno, what kind of architecture do you have? Can you share your config?
UPDATE
Seems that problem is connected directly with implementation of Spring Security Oauth2 Client in version science 5.0
Problem can be reproduced, if launch Zuul Gateway microservice on some separate virtual machine and other microservices should be launched at local machine ☝️ So Google should be called from the browser on VM.
The solution which helps me to avoid this problem is to add custom Filter with custom HttpServletRequestWrapper which can override method and return "right" URL to satisfy the check in OAuth2LoginAuthenticationProvider.java:115 😃
In the application.yml of the Oauth2 client
myCloudPath: ${MY_PROJECT_DOMAIN_NAME:http://localhost:8080}
In the SecurityConfig
#Value("${myCloudPath}")
private String myCloudPath;
#Override
public void configure(HttpSecurity http) throws Exception {
http.
addFilterBefore(new MyCustomFilter(myCloudPath), OAuth2LoginAuthenticationFilter.class).
...
Filter
public class MyCustomFilter implements Filter {
private static final Logger logger = LogManager.getLogger(MyCustomFilter.class);
private String myCloudPath;
public MyCustomFilter(String myCloudPath) {
this.myCloudPath= myCloudPath;
}
#Override
public void init(FilterConfig filterConfiguration) throws ServletException {
logger.info("MyCustomFilter init");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request = new MyHttpServletRequestWrapper((HttpServletRequest) request, myCloudPath);
chain.doFilter(request, response);
}
#Override
public void destroy() {
logger.info("MyCustomFilter destroy");
}
}
HttpServletRequestWrapper
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
public final String redirectUrl;
public MyHttpServletRequestWrapper(HttpServletRequest request, String myCloudPath) {
super(request);
this.redirectUrl = myCloudPath + request.getRequestURI();
}
#Override
public StringBuffer getRequestURL() {
return new StringBuffer(redirectUrl);
}
}
We faced the same issue while running in OpenShift and authenticating against Microsoft Azure. Filtering seemed like hacking, the *.redirect-uri-template properties are now deprecated, and after returning from Azure the outgoing and incoming redirect URIs did not match.
After much searching, this simple entry in application.properties solved the issue:
server.forward-headers-strategy=framework
I also faced this problem and nothing worked after trying all solutions available on google and stack overflow. This resolved the problem for me:
I upgraded the Spring-boot version to "2.3.1.RELEASE" and rebuilt the application. Then added this property in application.yml (talking about google here but i think it will work for facebook, okta and other identity provider platforms) :
security:
oauth2:
client:
registration:
google:
clientId: 'your google client id'
clientSecret: 'your google client secret'
redirect-uri: https://www.yourdomain.com/login/oauth2/code/google # important
Defining redirect-uri , did the work and that was only possible if you are working on a spring-boot 2.3.x and above release.
Don't waste your time on "redirect-uri-template" as you will be facing problem when google redirects the user-agent back to your application. Spring will still be authenticating the redirect-uri internally.
If you are working behind a reverse proxy (may be nginx) the this should be your server configuration :
location / {
absolute_redirect off;
proxy_ssl_server_name on;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_pass http://127.0.0.1:8080;
}
These settings on ngnix will make sure that Spring gets requests header which are coming in the request. I was using tomcat on port 8080 hence defined this in proxy_pass. You can change this for your application.
I hope this helps. Cheers.
For me works this.
I have set
redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"
and wrote custom filter which chnge http to https
#Slf4j
public class LinkedInRewriteFilter extends OncePerRequestFilter {
private static final String GET_PROTOCOL = "://.*";
private static final String LINKED_IN = "linkedin";
private static final String HTTPS = "https";
#Value("${base-url}")
private String baseUrl;
#Value("${server.port}")
private int serverPort;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request.getRequestURL().toString().contains(LINKED_IN)) {
request = new LinkedInHttpServletRequestWrapper(request);
}
filterChain.doFilter(request, response);
}
public class LinkedInHttpServletRequestWrapper extends HttpServletRequestWrapper {
public LinkedInHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
#Override
public String getScheme() {
return baseUrl.replaceFirst(GET_PROTOCOL, StringUtils.EMPTY);
}
#Override
public int getServerPort() {
return HTTPS.equals(getScheme()) ? 443 : serverPort;
}
}
}
For me, I had to do these:
Step 1: Change your config file
If using application.properties
server.port=8081
server.use-forward-headers: true
If using yml
server:
port: 8081
use-forward-headers : true
Step 2: Update proxy
If using httpd
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
ProxyPreserveHost On
ProxyPass / http://localhost:8081/
ProxyPassReverse / http://localhost:8081/
If using nginx
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Will add the user's ip to the request, some apps need this
proxy_set_header X-Forwarded-Proto $scheme; # will forward the protocole i.e. http, https
proxy_set_header X-Forwarded-Port $server_port; # Will forward the port
proxy_set_header Host $host; # !Important will forward the host address
proxy_pass http://localhost:8081/;
}
Step 3: Restart server and test
I think the new version of Spring Security just added new support for .redirect-uri replacing .redirect-uri-template.
Dependencies
I am using
org.springframework.boot:spring-boot-starter-web:2.3.2.RELEASE,org.springframework.boot:spring-boot-starter-oauth2-client:2.3.2.RELEASE,org.springframework.boot:spring-boot-starter-security:2.3.2.RELEASE.
Configuration File, e.g., .properties
In the configuration file, I could specify:
spring.security.oauth2.client.registration.google.redirect-uri=https://example.com/api/login/oauth2/code/google
Note that I did not use .redirect-uri-template
OAuth2 Flow
Then, Spring OAuth2 guides requests to the Google server with the URI like:
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount?response_type=...&
redirect_uri=https%3A%2F%2Fexample.com%2Fapi%2Flogin%2Foauth2%2Fcode%2Fgoogle
Note that redirect_uri=https%3A%2F%2Fexample.com has been encoded, where https:// protocol is used.
If you're using container apps or web apps contained over Linux, refer this answer. It could be caused by authentication redirecting to provider handle that's not served over HTTPS.

Protecting REST API with OAuth2: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active

I've been working for a few days to attempt to implement oauth2 protection on a REST API. I've tried a ton of different configurations but still haven't managed to get it to work.
I'm proving the code that I have right now, but I'm in no way married to this implementation. If you can show me some radically different way to accomplish what I want to accomplish, great.
My flow looks like this:
Client checks Auth Server, gets token.
Client sends token to Resource Server.
Resource Server uses Auth Server to make sure that token is valid.
The Auth Server works fine. I'm having trouble configuring the Resource Server.
Configs on Resource Server
Here's some of my configs. I have this bean:
Ouath Rest Template
#EnableOAuth2Client
#Configuration
#Import({PropertiesConfig.class}) //Imports properties from properties files.
public class OauthRestTemplateConfig {
#Bean
public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate template = new OAuth2RestTemplate(oauth2ResourceDetails(), oauth2ClientContext);
return template;
}
#Bean
OAuth2ProtectedResourceDetails oauth2ResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("theOauth");
details.setClientId("clientID");
details.setClientSecret("SecretKey");
details.setAccessTokenUri("https://theAuthenticationServer.com/oauthserver/oauth2/token");
details.setUserAuthorizationUri("https://theAuthenticationServer.com/oauthserver/oauth2/token");
details.setTokenName("oauth_token");
details.setPreEstablishedRedirectUri("http://localhost/login");
details.setUseCurrentUri(true);
return details;
}
}
Security Config
I use that bean in my main security config in Resource Server:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableOAuth2Client
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true, proxyTargetClass = true)
#Import({PropertiesConfig.class, OauthRestTemplateConfig.class})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("oAuth2RestTemplate")
private OAuth2RestTemplate oAuth2RestTemplate;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.accessDecisionManager(accessDecisionManager()) //This is a WebExpressionVoter. I don't think it's related to the problem so didn't include the source.
.antMatchers("/login").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().authenticated();
http
.exceptionHandling()
.authenticationEntryPoint(delegatingAuthenticationEntryPoint());
http
.addFilterBefore(new OAuth2ClientContextFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(oauth2ClientAuthenticationProcessingFilter(), OAuth2ClientContextFilter.class)
;
}
private OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter() {
OAuth2ClientAuthenticationProcessingFilter
daFilter = new OAuth2ClientAuthenticationProcessingFilter("/api/**");
daFilter.setRestTemplate(oAuth2RestTemplate);
daFilter.setTokenServices(inMemoryTokenServices());
return daFilter;
}
private DefaultTokenServices inMemoryTokenServices() {
InMemoryTokenStore tok = new InMemoryTokenStore();
DefaultTokenServices tokenService = new DefaultTokenServices();
tokenService.setTokenStore(tok);
return tokenService;
}
}
Extra stuff in Security Config
Aaand, some of the beans which I believe are less relevant, but here they are in case you need them:
#Bean
public DelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint() {
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> matchers =
Maps.newLinkedHashMap();
//Match all HTTP methods
matchers.put(new RegexRequestMatcher("\\/api\\/v\\d+\\/.*", null), oAuth2AuthenticationEntryPoint());
matchers.put(AnyRequestMatcher.INSTANCE, casAuthenticationEntryPoint());
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(matchers);
entryPoint.setDefaultEntryPoint(casAuthenticationEntryPoint());
return entryPoint;
}
#Bean(name = "casEntryPoint")
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(casUrl + "/login");
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
Error
Resource Server starts up just fine. Client gets its auth token from theAuthenticationServer.com and sends it in the request header to an api url. And I get the following error:
HTTP Status 500 - Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Exception report
Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
The server encountered an internal error that prevented it from fulfilling this request.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:355)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
com.sun.proxy.$Proxy26.getAccessToken(Unknown Source)
org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169)
org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:94)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
root cause
<pre>java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
com.sun.proxy.$Proxy26.getAccessToken(Unknown Source)
org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169)
org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:94)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
I've tried a lot of different configs, looked up a ton of resources online, and I've gotten nowhere. Am I using the right classes? Any idea what configs I might need to change?
An even easier way to enable the request context listener is to add a bean annotation into your app.
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
I ended up resolving this thing after looking into Spring documentation.
It turned out that the scope context didn't actually exist in my app, because I hadn't initialized it.
I initialized it by adding this listener:
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
I'm proving the code that I have right now, but I'm in no way married
to this implementation. If you can show me some radically different
way to accomplish what I want to accomplish, great
If your main problem is implementing the Resource Server and also, you are open to totally different solutions, you can use Spring Boot's resource server auto configurations. This way you would have a ResourceServerConfiguration such as following:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
// you can put your application specific configurations here
// here i'm just authenticating every request
}
}
With an application.yml config file in your src/main/resources:
security:
oauth2:
client:
client-id: client
client-secret: secret
resource:
token-info-uri: http://localhost:8888/oauth/check_token
You should add your client-id, client-secret and token-info-uri there. token-info-uri is the endpoint on Authorization Server that our resource server is going to consult about the validity of passed Access Tokens.
With these arrangements, if the client fire a request to, say, /api/greet API:
GET /api/greet HTTP/1.1
Host: localhost:8080
Authorization: bearer cef63a29-f9aa-4dcf-9155-41fb035a6cdb
Our resource server will extract the Bearer access token from the request and send the following request to the authorization server to validate the access token:
GET /oauth/check_token?token=cef63a29-f9aa-4dcf-9155-41fb035a6cdb HTTP/1.1
Host: localhost:8888
Authorization: basic base64(client-id:client-secret)
If token was valid, authorization server send a 200 OK response with a JSON body like following:
{"exp":1457684735,"user_name":"me","authorities":["ROLE_USER"],"client_id":"client","scope":["auth"]}
Otherwise, it will return a 4xx Client Error.
This was a maven project with a pom.xml like following:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
</dependencies>
And a typical Application class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You can check out the spring boot documentation on resource server auto configurations here.
I believe that the root of issue is that you create the OAuth2ClientAuthenticationProcessingFilter and OAuth2ClientContextFilter with new operator.
If you look at the stacktrace
org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
com.sun.proxy.$Proxy26.getAccessToken(Unknown Source)
org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169)
org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:94)
there's a chain how it goes from OAuth2ClientAuthenticationProcessingFilter to JdkDynamicAopProxy and tries to get the bean. And I can assume because of that bean was created out of Spring container, it can't get the bean from the session scope.
Try to wrap your filters into #Bean annotation so to put them into context. Also, i believe it worth being set the correct scope: the request would match best here.
I faced the same problem when using spring-boot 1.4.1 with spock-spring 1.1-groovy-2.4-rc-2. The easiest way to fix it is to use Spock 1.0.
There is a bug reported already:
https://github.com/spockframework/spock/issues/655
public class ContextAwareCallable implements Callable {
private Callable<T> task;
private RequestAttributes context;
public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
this.task = task;
this.context = cloneRequestAttributes(context);
}
#Override
public T call() throws Exception {
if (context != null) {
RequestContextHolder.setRequestAttributes(context);
}
try {
return task.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
private RequestAttributes cloneRequestAttributes(RequestAttributes requestAttributes) {
RequestAttributes clonedRequestAttribute = null;
try {
clonedRequestAttribute =
new ServletRequestAttributes(((ServletRequestAttributes) requestAttributes).getRequest(),
((ServletRequestAttributes) requestAttributes).getResponse());
if (requestAttributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST).length > 0) {
for (String name : requestAttributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST)) {
clonedRequestAttribute.setAttribute(name,
requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST),
RequestAttributes.SCOPE_REQUEST);
}
}
return clonedRequestAttribute;
} catch (Exception e) {
return requestAttributes;
}
}
}
Then create executor
public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
#Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}
#Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}
}

Spring 3 -- NullPointerException from JDBCTemplate.getQueryForMap()

I am running a custom tag in a Spring JSP. I am getting the following error message.
java.lang.NullPointerException
com.dao.Poll1DAO.getVotes(Poll1DAO.java:27)
com.tags.Poll1Tag.doTag(Poll1Tag.java:23)
org.apache.jsp.WEB_002dINF.pages.PollPage_jsp._jspx_meth_myTag1_005fpoll1_005f0(PollPage_jsp.java:602)
org.apache.jsp.WEB_002dINF.pages.PollPage_jsp._jspService(PollPage_jsp.java:195)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:238)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:262)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1265)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1016)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:965)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:859)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:883)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:781)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
Here is Poll1DAO.getVotes().
public HashMap<String, Object> getVotes() {
// following line is line 27, referenced in error message
return (HashMap<String, Object>) this.getJdbcTemplate().queryForMap(
"select yes, no from poll1 where id = 1");
}
Here is Poll1Tag.doTag().
Poll1DAO poll1DAO = new Poll1DAO();
public void doTag() throws JspException, IOException {
PageContext pageContext = (PageContext) getJspContext();
boolean foundCookie = cookieFound();
pageContext.setAttribute("foundCookiePoll1", foundCookie);
if (foundCookie) {
// following line is line 23, referenced in error message
HashMap<String, Object> poll1Votes = poll1DAO.getVotes();
pageContext.setAttribute("poll1Yes", (int) poll1Votes.get("yes"));
pageContext.setAttribute("poll1No", (int) poll1Votes.get("no"));
}
}
If you are using IoC container such as Spring's application context you should never create managed bean instances by yourself. You always need to pull the instance out from the container - i.e. ApplicationContext. Otherwise they will not be initalized with the necessary dependencies.
Now the question you should ask is - how can I get ApplicationContext instance in a JSP tag class. The answer is simple - just look how the standard Spring tags are doing that (I mean that - go check that link).
// Example on how to obtain bean from JSP tag (ignoring context hierarchy)
WebApplicationContextUtils.getRequiredWebApplicationContext(pageContext.getServletContext()).
getBeansOfType(Poll1DAO.class).values().iterator().next();

Resources