How to create servlet filter to authorize the request? - spring

How to create spring servlet filter to authorize the request.
Need to add a filter in the spring security filter chain that updates the user with few details (re-loading permissions or anything) for every request, if they need to be changed.
Need some sample code snippet to follow or understand.
Thanks in advance.

To add a custom filter you should extend the org.springframework.web.filter.GenericFilterBean, like so:
public class MySecurityFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
The next step is to actually register the filter by overriding the configure method of WebSecurityConfigurerAdapter:
#Configuration
public class CustomWebSecurityConfigurerAdapter
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(
new MySecurityFilter(), BasicAuthenticationFilter.class);
}
}
As you can see the filter is added by adding the filter to the HttpSecurity object. The method that is used is the addFilterAfter which basically allocates your filter after the one that you provide in the second argument, in this example is the BasicAuthenticationFilter, so your filter will be executed after this one, in the spring secuirty chain of filters.
Update
Refer to this link to personalize the authorization of your servlet. The method gives you an Authentication object, through which you can obtain your User object and perform additional checks.

Related

what's the proper way to add Spring Security filter only for specific URL? [duplicate]

This question already has answers here:
Filter invoke twice when register as Spring bean
(3 answers)
Closed 5 months ago.
I am trying to add custom filter to only specific URL, however the filter get applied to every request, regardless of URL and method, does anybody know the proper way to fix this using latest from Spring Security, i.e. not using WebSecurityConfigurerAdapter, because it is going to be deprecated.
There are many similar questions here, but they either do not work for me, or they use the "old" approach (like this and this and many other), or both.
I have number of endpoints exposed that all follow the pattern: /api/** however I need to provide some authentication for a specific endpoint: /api/some/url and a particular method (GET in this case), how do I do this properly?
NOTE: the endpoint URLs are all under /api/* (should they be called nested?)
My security configuration looks like this:
#EnableWebSecurity
public class SecurityConfig {
private MyFilter myFilter;
public SecurityConfig(MyFilter pif) {
myFilter = pif;
}
/**
* Handling AuthZ & AuthN for most APIs. No AuthZ & AuthN.
*/
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain defaultSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((requests) ->
requests.antMatchers("/"))
.authorizeHttpRequests((authorize) -> authorize.anyRequest()
.permitAll());
return http.build();
}
/**
* Handling AuthZ & AuthN for GET /api/some/url.
*/
#Bean
public SecurityFilterChain keyApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((requests) -> requests
.antMatchers(HttpMethod.GET, "/api/some/url").and())
.addFilterBefore(myFilter,
BasicAuthenticationFilter.class)
.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll());
return http.build();
}
}
When you expose some GenericFilter implementation as a bean in spring-boot, it automatically puts it in a common filter chain for any request, because it doesn't know if it's a security filter or not - it could be a logging filter or anything else.
So this filter bean will be executed regardless of spring-security.
Your defaultSecurity filter chain doesn't have this custom filter, so MyFilter will be executed after spring security filter chain due to the order.
At the same time, keyApiSecurity filter chain sets this custom filter before BasicAuthenticationFilter, so it will be executed there, and will not be executed the second time, because basic doFilter() implementation of OncePerRequestFilters method checks whether the request was already filtered by the filter.
So, if you want your filter to work only as a security filter, you should not expose it as a bean, and you should set it in a security filter chain like this:
.addFilterBefore(new MyFilter(), BasicAuthenticationFilter.class)
Also you should think about setting the lowest priority for a "default" security filter chain, because if it's selected first - other security filter chains will be totally ignored. So I think some specific filter security chains should have higher priority.
EDIT:
If you can't set your security filter with the new operator because you rely on bean injection in this Filter implementation, you can override shouldNotFilter(HttpServletRequest request) method of OncePerRequestFilter, for example like this:
#Component
public class MyFilter extends OncePerRequestFilter {
private final RequestMatcher uriMatcher =
new AntPathRequestMatcher("/api/some/url", HttpMethod.GET.name());
// some bean injection...
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// filter logic here...
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
RequestMatcher matcher = new NegatedRequestMatcher(uriMatcher);
return matcher.matches(request);
}
}
Then it will filter only matched requests, and putting it into the security filter chain will set its order.

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);
}
...

Configure Spring Security to return JSON response after authentication

I have a legacy application that is in spring 4 with UI as JSP. Need move the presentation layer from spring to react app. When I call /login with parameters it gives me an HTML, How do I change my existing spring security logic so that it returns a json response.
Here is the code snippet
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(1).and().invalidSessionUrl(URLConstants.LOGIN_URL);
http.csrf().disable();
http.anonymous().disable()
.authorizeRequests().antMatchers("/")
.access("hasRole('USER') or hasRole('ADMIN') or hasRole('DC MANAGER')")
.and().formLogin() .loginProcessingUrl(URLConstants.LOGIN_URL).usernameParameter("ssoId").passwordParameter("password").and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository) .tokenValiditySeconds(18400).and().exceptionHandling().accessDeniedPage("/Access_Denied");
}
Write a custom AuthenticationSuccessHandler that writes your JSON and plug it into your formLogin().
.formLogin().successHandler(yourSucessHandlerBean);
Your handler could roughly look like this:
#Component
public class Securityhandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
// write your JSON here, directly to the HttpServletResponse
}
}

configuring interceptors to perform "pre-tasks" on hitting endpoints in spring based web app

I am required to perform some pre-task whenever few specific endpoints are hit in my spring based web app. I came across the interceptor component provided by the spring-security framework. I searched various forums but didn't find relevant answers for configuring and adding interceptor.
Consider a situation where I am required to set some key=value in a database by sending POST request in the database whenever the user hits following endpoints.
/endpoint1
/endpoint2
/endpoint3
/endpoint4
After completion of the pre-task user should be redirected to the origin endpoint.
How can this be achieved using an interceptor in the spring-security framework?
Spring Security is for security stuff related to Authentification and Authorization. You can trigger some action if somebody logged in, but if you just need to trigger action for each request than Spring Security is not a good place for that (according to business logic), better add just filter. Anyway answering to your question:
The best way is to add custom filter to Spring Security Filter Chain:
You have to overwrite:
#Configuration
public class CustomWebSecurityConfigurerAdapter
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(
new CustomFilter(), BasicAuthenticationFilter.class);
}
}
and create your custom filter:
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//your logic here
chain.doFilter(request, response); //it's needed to pass your request father
}
}
Code taken from baeldung.com see for more information

Spring Security: How can I set a RememberMe cookie url path, that differs from the context path?

How in Spring Security can I set a RememberMe cookie url path, that differs from the context path?
Supposing my website's homepage url is (url rewrite):
https://www.mysuperspecialdomain.com
And that my login page has a url like this:
https://www.mysuperspecialdomain.com/shop/account/login
After succesful login the RememberMe cookie has the path /shop (visible in the browser, e.g. Chrome). This is the project's context path.
This leads to the situation, that when I'm going to my homepage, RememberMe is not logging in. Only when I navigate to a url, that starts with https://www.myspecialdomain.com/shop it's doing it.
If you use Spring Security 4.1.0 or higher, you can configure the cookie domain, see RememberMeConfigurer#rememberMeCookieDomain:
The domain name within which the remember me cookie is visible.
but you can't change the context path.
So you have to implement your own RememberMeServices (you could create a sub class of an existing one) and add it with RememberMeConfigurer#rememberMeServices to your security configuration.
I've found a solution to my own question - manipulation of the path of the RememberMe-cookie can be done via an HttpServletResponseWrapper. This is the solution (based on this answer https://stackoverflow.com/a/7047298/7095884):
Define an HttpServletResponseWrapper:
public class RememberMeCookieResponseWrapper extends HttpServletResponseWrapper {
public RememberMeCookieResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void addCookie(Cookie cookie) {
if (cookie.getName().equals("shop")) {
cookie.setPath("/");
}
super.addCookie(cookie);
}
}
Define a filter, that wraps the servlet response with the just defined wrapper:
public class RememberMeCookieFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (response instanceof HttpServletResponse) {
HttpServletResponse newResponse =
new RememberMeCookieResponseWrapper((HttpServletResponse)response);
chain.doFilter(request, newResponse);
}
}
}
Add this filter to the Spring Filter Chain in front of the authentication part:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new RememberMeCookieFilter(), UsernamePasswordAuthenticationFilter.class)
...

Resources