Passing a new header in Spring Interceptor - spring

I want to add authentication logic to interceptor. When service is called, interceptor will authenticate. Once authenticated, I want to put a new header in the request say 'header-user': 'john-doe'. But in interceptor, I am unable to do that, when I add to response.setHeader(), nothing happens.
I want to use this new header in actual REST service.
public class AuthInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Authenticate
// Add header
response.setHeader("header-user", "john-doe"); // not working
return true;
}
...
}
If I add Filter, filter is called before Interceptor.

I figured out from Using Spring Interceptor that I can use setAttribute
request.setAttribute("user", "john-doe");
In controller side use,
public String testService(#RequestAttribute("user") String user){

Related

Add response header in HandlerInterceptorAdapter

I am adding a header to the response inside HandlerInterceptorAdapter.
However it seems that the response header cannot be modified in the postHandle method.
public class CredentialInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true;
}
#Override
public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) {
String value = "...";
response.addHeader("header_name",value ); // doesn't work
}
}
How to add a header to the response ?
Popular solution is to use OncePerRequestFilter ( Set response header in Spring Boot ). Isn't there any other way ?
The problem with adding headers in the postHandle method is that the response may already be (partially) send. When that is the case you cannot add/change headers anymore. You need to set the headers before anything is sent to the client.
This you can do in the preHandle method or more generic a servlet filter before you call filterchain.doFilter. Doing it after the aforementioned call you might get the same issue that a response has already (partially) been sent.

Spring authentication scheme using the authentication header

I am using a spring boot application and I have a web security config adapter set up to authenticate each request using the jwt.
I want to expand my service to allow a different api end point to be authenticated using the header. One of the services I am integrating with sends a web hook and all it sends is the request with the custom header I set it up to include. How can I set up a specific endpoint to only authenticate using a custom header?
You could use a OncePerRequestFilter to filter the requests to that endpoint and return a 401 if they are do not contain your header with the right value.
You would define your filter:
public class HeaderSecurityFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String value = request.getHeader("Token");
if(value == null || !value.equals("Secret")) {
response.sendError(401);
return;
}
chain.doFilter(request, response);
}
}
And then register it:
#Configuration
public class HeaderSecurityConfiguration {
#Bean
FilterRegistrationBean<HeaderSecurityFilter> filterRegistration() {
FilterRegistrationBean<HeaderSecurityFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new HeaderSecurityFilter());
registration.addUrlPatterns("/some/path/*");
return registration;
}
}
Which would require the header of Token be present with a value of Secret for anything under /some/path/*.
You would also need to ensure through your oauth configuration that you open up access to /some/path/*.

Is possible ask for an acces token oauth2 just with refresh token in spring security? without basic authentication?

I would like to know if in spring oauth2 is possible get a new pair tokens (access token and refresh token) just using another refresh token, without the basic authentication (without clientId and clientSecret, is there any way?
For exemple:
WITH BASIC AUTH
curl -u clientId:clientSecret -X POST 'http://myapplication.oauth2/accounts/oauth/token?grant_type=refresh_token&client_id=<CLIENT_ID>&refresh_token=' -v
WITHOUT BASIC AUTH
curl -u -X POST 'http://myapplication.oauth2/accounts/oauth/token?grant_type=refresh_token&client_id=<CLIENT_ID>&refresh_token=' -v
I note that sprint BasicAuthenticationFilter in spring uses validation bellow, maybe override this filter and make the authentication just with refresh token.
String header = request.getHeader("Authorization");
if (header == null || !header.toLowerCase().startsWith("basic ")) {
chain.doFilter(request, response);
return;
}
The short answer is no. The class used to manage the Spring Oauth 2 endpoints is the following one:
#FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint
Both requests, I mean, get access token and refresh one use the same endpoint with different parameters. And the method to manage those ones is:
#RequestMapping(
value = {"/oauth/token"},
method = {RequestMethod.POST}
)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, #RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
} else {
String clientId = this.getClientId(principal);
...
As you can see, a Principal object is required (in this case provided by the Basic Authentication).
Even, if you configure the security of your project to permit that url without checking authentication, you will achieve to "enter" in above method but you will receive an InsufficientAuthenticationException because no Authentication instance has been provided.
Why custom authentication will not work
1. Create a custom AuthenticationProvider will not work because the method postAccessToken is invoked before. So you will receive an InsufficientAuthenticationException.
2. Create a OncePerRequestFilter and configure it to execute before process the current request:
#Override
protected void configure(HttpSecurity http) throws Exception {
http...
.anyRequest().authenticated()
.and()
.addFilterBefore(myCustomFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(POST, "/accounts/oauth/**");
}
with a code "similar to":
#Component
public class CustomAuthenticationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
...
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("existingUser",
"passwordOfExistingUser",
Collections.emptyList()));
...
filterChain.doFilter(request, response);
}
The problem with this approach is the principal in TokenEndpoint comes from the HttpServletRequest not from Spring context, as you can see debugging BasicAuthenticationFilter class.
In your custom filter you can try, using reflection, set a value in userPrincipal property but, as you can verify, request has several "internal request properties" and that could be a "too tricky option".
In summary, Oauth standard needs user/pass to access to the resources, if you want to workaround in almost of provided endpoints maybe that project is not what you are looking for.
Workaround to include your own object in Spring Principal
I do not recommend that but if you still want to go ahead with this approach, there is a way to include your own value inside the principal parameter received by TokenEndpoint class.
It is important to take into account BasicAuthorizationFilter will be still executed, however you will be able to override the Spring principal object by your own one.
For this, we can reuse the previous CustomAuthenticationFilter but now your have to include the filters you need, I mean, allowed urls, parameters, etc You are going to "open the doors", so be careful about what you allow and not.
The difference in this case is, instead of add the configuration in our class that extends WebSecurityConfigurerAdapter we are going to do it in:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private CustomAuthenticationFilter customAuthenticationFilter;
...
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
security.addTokenEndpointAuthenticationFilter(customAuthenticationFilter);
}
...

With Spring Security how do i determine if the current api request should be authenticated or not?

With spring security you can have public api endpoints that are accessible by everyone and endpoints that need to be authenticated before getting a response. In my app users authenticate via a jwt token. For logged in users right now the token is always checked, regardless of whether a public api endpoint gets the request or not.
I would like to know how to check if the current endpoint is a public endpoint or a authenticated one, that way i can alter the code so that the token checking is only done when the endpoint requires authentication.
I could add all public endpoints in a hashset and compare the current request endpoint with the public ones but that isn't efficient and also, some of the public endpoints contain wildcards (**) so that would make comparing a bit of a hassle.
This is the only information i could find:
Spring Security - check if web url is secure / protected
but its about JSP.
I can't get the request information from SecurityContextHolder.getContext() either. My guess is that i should get the information from org.springframework.security.config.annotation.web.builders.HttpSecurity because that is the same class used to define which endpoints don't require authentication. (with anthMatchers().permitall()). But i don't know which method to invoke and i'm not sure if HttpSecurity can even be autowired into another class. Can anyone give me some pointers?
Thank you
Assuming that you're using a separate filter for the token check, you can avoid the token check for public endpoints by overriding the protected boolean shouldNotFilter(HttpServletRequest request) method of the OncePerRequestFilter in your JwtTokenFilter. By default, this method will always return false. So all requests will get filtered. Overriding this method to return true for the public endpoints will give you the desired functionality.
And to check the requests with the wildcards(**), you can use AntPathRequestMatcher. So, you can do something like below.
public class JwtTokenFilter extends OncePerRequestFilter {
private static RequestMatcher requestMatcher;
public static void ignorePatterns(String... antPatterns) {
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, null));
}
requestMatcher = new OrRequestMatcher(matchers);
}
static {
final String[] publicEndPoints = {"/public-api/**","/resources/**"};
ignorePatterns(publicEndPoints);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return requestMatcher.matches(request);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
....
}
}
Hope this helps!!

spring mvc: applying #ModelAttribute on non-#Controller endpoints

I've read this suggestion on using #ModelAttribute for injecting parameters to the model globally. Is my understading correct, that such an approach will not cover views rendered by, e.g. <mvc:view-controller>, or a form-login custom login page?
If so, is there a way to extend such a mechanism to include all views?
Thanks
Ended-up using an Interceptor, as laid-out in this reply. Registered interceptor to intercept all non-resource endpoints (using mvc:exclude-mapping).
public class HandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
if (modelAndView == null)
return;
modelAndView.addObject("foo", "bar");
}

Resources