Security filters are overlapping - spring

When multiple filters are added to the HttpSecurity configure method, they seem to be overlapping because only one works at the time.
This is the configure method:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.logout().and().antMatcher("/**")
.addFilterBefore(ssoFilter(), RequestHeaderAuthenticationFilter.class)
.authenticationProvider(preauthAuthProvider())
.authorizeRequests()
.antMatchers("/index.html", "/home.html", "/", "/login").permitAll()
.anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
I've tried to specify the order but the issue still persists:
#Bean
public FilterRegistrationBean securityFilterChain(#Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) Filter securityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(Integer.MAX_VALUE - 2);
registration.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
return registration;
}
#Bean
public FilterRegistrationBean ssoFilterRegistrationBean() throws Exception {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(ssoFilter());
registrationBean.setOrder(Integer.MAX_VALUE-1);
return registrationBean;
}
#Bean
public FilterRegistrationBean csrfFilterRegistrationBean() throws Exception {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(csrfHeaderFilter());
registrationBean.setOrder(Integer.MAX_VALUE);
return registrationBean;
}
I've followed the following thread with no success.
Filter order in spring-boot
https://github.com/spring-projects/spring-boot/issues/1640
https://github.com/spring-projects/spring-boot/issues/677
Any help will be appreciated!
UPDATE:
CSRF Filter definition
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request
.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null
|| token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
SSO Filter definition:
public class SSORequestHeaderAuthenticationFilter extends RequestHeaderAuthenticationFilter {
private boolean allowPreAuthenticatedPrincipals = true;
public SSORequestHeaderAuthenticationFilter() {
super();
//TODO Pull this value from a properties file (application.properties, or localstrings.properties)
//NOTE SM_USER is the default, but you can change it like this (your company may use some other header)
//this.setPrincipalRequestHeader("SM_USER");
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
/**
* This is called when a request is made, the returned object identifies the
* user and will either be {#literal null} or a String. This method will throw an exception if
* exceptionIfHeaderMissing is set to true (default) and the required header is missing.
*
* #param request {#link javax.servlet.http.HttpServletRequest}
*/
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String userName = (String) (super.getPreAuthenticatedPrincipal(request));
if (userName == null || userName.trim().equals("")) {
return userName;
}
return userName;
}
public boolean isAllowPreAuthenticatedPrincipals() {
return allowPreAuthenticatedPrincipals;
}
}

My guess is that you are not always executing FilterChain.doFilter method inside both filters. Then the filter chain stops and only one of your custom filters is executed. In this simple example both filters executed:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new Filter1(), RequestHeaderAuthenticationFilter.class)
.addFilterAfter(new Filter2(), CsrfFilter.class)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
static class Filter1 extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("executed filter 1");
filterChain.doFilter(request, response);
}
}
static class Filter2 extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("executed filter 2");
filterChain.doFilter(request, response);
}
}
}

You are confusing container registration (with FilterRegistrationBean) and registration in a Security filter chain (with HttpSecurity) and also possibly with the order of the filter chains within Spring Security. If a given filter chain is selected by Spring Security all the filters in it are not even necessarily fired anyway (filters can always switch off other downstream filters).
I suggest you stop worrying about the order in your FilterRegistrationBeans and use them to disable the container registration (by setting their enabled flag to false). Then think about the order of your filter chains, as specified by the #Order on your WebSecurityConfigurers. And finally you can decide if the order of the filters in a given chain matters, and if it does use the addFilter{Before,After} methods.

A Filter which does what you want to do does already exist. Its the OAuth2AuthenticationProcessingFilter.
This filter is used if you annotate your application with #EnableResourceServer. If you do so this will cause, that only token based authentication will work now.
You have to set the stateless flag of this filter to false to allow other ways of authentication, too.
What i did is to create a class ApiTokenAccessFilter which extends OAuth2AuthenticationProcessingFilter. This filter takes a ResourceServerTokenServices constructor parameter and sets the stateless flag to false.
public class ApiTokenAccessFilter extends OAuth2AuthenticationProcessingFilter {
public ApiTokenAccessFilter(ResourceServerTokenServices resourceServerTokenServices) {
super();
setStateless(false);
setAuthenticationManager(oauthAuthenticationManager(resourceServerTokenServices));
}
private AuthenticationManager oauthAuthenticationManager(ResourceServerTokenServices tokenServices) {
OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
oauthAuthenticationManager.setResourceId("oauth2-resource");
oauthAuthenticationManager.setTokenServices(tokenServices);
oauthAuthenticationManager.setClientDetailsService(null);
return oauthAuthenticationManager;
}
}
In my security config i used this Filter as follows:
#Configuration
#EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private ResourceServerTokenServices tokenServices;
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(new ApiTokenAccessFilter(tokenServices), AbstractPreAuthenticatedProcessingFilter.class);
}
}
I think this could be easier so i opened an issue on the spring-security-oauth Github repo. I'm not sure whether this solution is the way to go, but i didn't find another alternative.

Related

Custom Authentication using AbstractAuthenticationProcessingFilter with permitted paths and Also enabling method security with roles

I am trying to implement a custom token based authentication with authentication filter:
public class AuthAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws AuthenticationException,
IOException, ServletException {
//some code here
}
#Override
protected void successfulAuthentication(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain) { final Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
Registering it with
#Configuration
#Order(2)
#EnableWebSecurity(debug = true)
public class Security extends WebSecurityConfigurerAdapter {
// constructor and beans etc
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.requestMatchers(PROTECTED_URLS).hasAnyRole(getAllrolles()).anyRequest()
.authenticated()
.and()
.addFilterBefore(authAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
}
I have another websecurity config with order 1 for whitelisted urls:
#Configuration
#Order(1)
public class SecurityWhiteList extends WebSecurityConfigurerAdapter {
//constructor and permitted path etc
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.requestMatchers(permittedPaths()).permitAll()
.and()
.anonymous();
}
}
At last, I have a configuration for method security as follows:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurity extends GlobalMethodSecurityConfiguration {
private final RoleHierarchy roleHierarchy;
#Autowired
public MethodSecurity(RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new
DefaultMethodSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
}
}
The problem here is, I get 403 for the endpoints having method security in the service method annotated with #PreAuthorize("hasAuthority('ROLE_SOMETHING')) even though the users do have ROLE_SOMETHING.
It works, if #PreAuthorize("hasAuthority('ROLE_SOMETHING')) is not present and roles authentication, permitted path everything works.
After debugging, I found that my method security runs before AuthAuthenticationFilter and thus the security context is not populated yet so the user gets ROLE_ANONYMOUS.
My question is: Is it possible to make the authentication filter fire before method security? Or am I entirely doing it wrong? Like I should instead use oncePerRequestFilter instead of AbstractAuthenticationProcessingFilter?

How to configure two security configs with two filters in spring boot correctly?

I've implmemented security in my spring boot microservices project, the requirment is to have
two types of configurations, one for user request (from angular) and one from other services.
The design is to use JWT token for user request and API key for system calls.
Here is the config file (one file) but have also try to split it to two files with no impact:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(1)
public static class APISecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${my.api.key.header}")
private String principalRequestHeader;
#Value("${my.api.key.token}")
private String principalRequestValue;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable().csrf().disable();
httpSecurity
.antMatcher("/api/users/**")
.authorizeRequests() //
.anyRequest().authenticated()
.and()
.addFilterBefore(new APIKeyAuthFilter(principalRequestHeader, principalRequestValue), UsernamePasswordAuthenticationFilter.class);
}
}
#Order(2)
#Configuration
public static class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/users/**");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable().csrf().disable();
httpSecurity
.authorizeRequests()
.antMatchers("/users/UserEmailExist", "/users/User/Add", "/users/Authenticate",
"/users/User/ChangePassword")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/users/**").hasAnyRole(ROLE_ADMIN_USER, ROLE_MANAGER_USER)
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
}
Each config has a filter attached to it, here the api one:
public class APIKeyAuthFilter extends GenericFilterBean {
private String principalRequestHeader;
private String principalRequestValue;
public APIKeyAuthFilter(String principalRequestHeader, String principalRequestValue) {
super();
this.principalRequestHeader = principalRequestHeader;
this.principalRequestValue = principalRequestValue;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if(request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
String apiKey = getApiKey((HttpServletRequest) request);
if(apiKey != null) {
if(apiKey.equals(principalRequestValue)) {
ApiKeyAuthenticationToken apiToken = new ApiKeyAuthenticationToken(apiKey, AuthorityUtils.NO_AUTHORITIES);
SecurityContextHolder.getContext().setAuthentication(apiToken);
} else {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(401);
httpResponse.getWriter().write("Invalid API Key");
return;
}
}
}
chain.doFilter(request, response);
}
}
Here is the filter for jwt (normal user from angular):
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired
private JwtUtils jwtUtils;
#Autowired
private MyUserDetailsService userDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
MSUserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
}
I've created two different controllers, one with prefix /api/users and second /users.
Here is what happen in two different scenarios:
The user login from Angular, get jwt token and process request which end up in the Jwt filter,
this scenarion looking good with no issues as the user is able to process request as long
he is authenticate.
Microservice send a request with api-key to url with /api/users prefix, it ended up on the same
filter the normal user ended which is not correct and without JWT token he is actually
able to proceed to the controller and process the request without going
to the correct filter.
The only solution I have is to have only one filter and process the header
for api-key and jwt but it doesn't seem right.
I've looked online and try to figure out what I'm doing wrong but no clue as of now.
An update on this issue so I hope it will help to the community.
Firstly, I removed the following code and this mainly fix the problem:
// #Override
// public void configure(WebSecurity web) throws Exception {
// web.ignoring().antMatchers("/api/users/**");
// }
The way the solution work as a whole is that the first configuration #Order(1) you
define .antMatcher which means the configuration will work only for urls that match
the prefix.
So now, scenario 1. User from Angular go the the JWT filter only.
scenario 2. API user will lend in the API filter first! But once it's done (After succesfull authentication) it still
continue to the JWT filter but becuase it doesn't have JWT the filter not doing anything.
I would like to avoid to other filter in case of API call but the solution work,
problem solved.
I must say that security in spring boot is the most complex I came across so far from other features.
Because the AuthTokenFilter is instantiated with #Bean, which causes the filter to be added to the ApplicationFilterChain, after the APIKeyAuthFilter is processed, it can also enter the AuthTokenFilter.

How to apply a security filter only on a restricted http path [duplicate]

This question already has answers here:
Disabling a filter for only a few paths in spring security
(2 answers)
Closed 1 year ago.
I have a problem with my Spring security configuration. I just want to basically apply an authentication filter to some paths, and not to other path. But the filter i have defined is applied on all the HTTP request ever what i write in the configuration.
Here is my code.
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtFilter jwtFilter;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("{bcrypt}$2a$10$DmzAlIznZz3faNQx1eBTBOw6fNiGE105fKoHkvskYTMXH5OFUE6iy")
.roles("USER");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests() //
.anyRequest().authenticated() //
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
JWTFilter:
#Component
public class JwtFilter extends OncePerRequestFilter {
#Autowired
private JWTUtils jwtUtils;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException {
String authorizationHeader = httpServletRequest.getHeader("Authorization");
String token = null;
String userName = null;
if (authorizationHeader != null) {
userName = jwtUtils.extractUsername(token);
}
if (jwtUtils.validateToken(token)) {
} else {
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userName, null);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
For example if i try to call this servlet:
#PostMapping("/login")
public ResponseEntity<UserDetails> login(#RequestBody User user) throws Exception {
try {
Authentication authenticate = authenticate(user.getName(), user.getPassword());
UserDetails authenticatedUser = (UserDetails) authenticate.getPrincipal();
return ResponseEntity.ok()
.header(
HttpHeaders.AUTHORIZATION,
generateToken(authenticatedUser.getUsername())
)
.body(authenticatedUser);
} catch (BadCredentialsException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
My filter is called to verify if the client is authenticated but it is my login end point so my client is accordingly not authenticated yet...
For me the code I found on internet that should resolve this problem is this one:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests() //
.anyRequest().authenticated() //
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
You can find this code in the security config.
To exclude urls for the security filter, you should override the other configure method that accepts a WebSecurity as an argument and specify the url paths to ignore...
eg.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/login/**");
}
Another option you could look into is to configure form based login in spring security...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
...
.logout();
}
It also looks like you're custom coding oauth2 authentication. Have you looked at what spring security 5 provides out of the box for securing urls with jwt tokens?
Check out the documentation at
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2resourceserver.

Spring security bypass if has different Authorization header

I have implemented spring security with jwt and is working fine.
#Configuration
#Component
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomJWTProvider jwtTokenProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/health").permitAll()
.anyRequest().authenticated()
.and()
.apply(new CustomJwtConfigurer(jwtTokenProvider));
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
}
But i have a typical use case where i get jwt token or 64bit fixed length token in Authorization header.
If it is JWT token, spring security should work as is and if it is not, the control should be passed to some method (where i have my logic to validate that token)
My filter looks like
#Component
public class CustomFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader("Authorization");
if(myCustomValidator(authToken)){
//if this is true it should skip spring security jwt token verification
//which is in configure phase
}else{
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(403, "Access denied");
}
System.out.println("authToken="+authToken);
filterchain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
}
How/Where can i add this filter to bypass spring security, or any other mechanism to skip spring security based on authorization header?
You can use OncePerRequestFilter. You need to create a class and extend this class where you override its method(doFilterInternal) and put your logic there.
You can configure that filter like below.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class YourClass extends WebSecurityConfigurerAdapter {
#Autowired
private YourFilter authFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(authFilter, UsernamePasswordAuthenticationFilter.class);
}
}

How does Spring Security Filter Work With Custom Authentication and How To Combine It with Servlet Filter?

So I have a question regarding Spring Security. So I want to check authentication using custom header which then I want to check the token given in the custom header to redis value and set the data object as credentials at custom implementation of abstract authentication token.
I have already followed the tutorial in this web: https://shout.setfive.com/2015/11/02/spring-boot-authentication-with-custom-http-header/, but I can't update the authentication interface in SecurityContextHolder.getContext() (I set the credentials in my implementation of Authentication Interface, but when I get it in the service, the credentials is null).
I also found other problems, I actually want to order the filter like this:
ExceptionHandlerFilter (to catch exception error in the filter) -> Other filter or CustomWebSecurityConfigurerAdapter.
But when the url matches the antMatcher, I found that ExceptionHandlerFilter was skipped by the application.
I was so confused by this and could not find better tutorial in implementing custom authentication using Spring Security. So I want to ask whether you guys can tell me how Spring Security works and how to combine it with Filter?
Here is my first filter to catch exception
#Component
#Order(0)
public class ExceptionHandlerFilter extends OncePerRequestFilter {
private JaminExceptionHandler exceptionHandler;
private ObjectMapper objectMapper = new ObjectMapper();
#Autowired
public ExceptionHandlerFilter(JaminExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (Throwable exception) {
ResponseEntity<?> responseEntity = this.exceptionHandler.handleException(exception, request);
response.setStatus(responseEntity.getStatusCode().value());
response.setHeader("Content-Type", "application/json");
response.getWriter().write(this.objectMapper.writeValueAsString(responseEntity.getBody()));
}
}
}
Here is my Auth Filter
#Component
public class AuthFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("J-Auth");
if (token != null) {
Authentication auth = new JaminAuthenticationToken(token);
SecurityContextHolder.getContext().setAuthentication(auth);
filterChain.doFilter(request, response);
} else {
throw new JaminException("Not authorized", JaminExceptionType.NOT_AUTHORIZED, HttpStatus.UNAUTHORIZED);
}
}
}
Authentication Provider
#Component
public class JaminAuthenticationProvider implements AuthenticationProvider {
private RedisTemplate<String, String> authRedis;
private ObjectMapper objectMapper = new ObjectMapper();
#Autowired
public JaminAuthenticationProvider(#Qualifier("authRedis") RedisTemplate<String, String> authRedis) {
this.authRedis = authRedis;
}
private UserDTO getUserDTO(String token) throws IOException {
String userData = this.authRedis.opsForValue().get(token);
if (userData == null) {
throw new JaminException("Not authorized", JaminExceptionType.NOT_AUTHORIZED, HttpStatus.UNAUTHORIZED);
}
return this.objectMapper.readValue(userData, UserDTO.class);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JaminAuthenticationToken auth = (JaminAuthenticationToken) authentication;
try {
UserDTO userDTO = this.getUserDTO(auth.getToken());
auth.setCredentials(userDTO);
return auth;
} catch (IOException e) {
e.printStackTrace();
}
throw new JaminException("Not authorized", JaminExceptionType.NOT_AUTHORIZED, HttpStatus.UNAUTHORIZED);
}
#Override
public boolean supports(Class<?> authentication) {
return JaminAuthenticationToken.class.isAssignableFrom(authentication);
}
}
WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
#Order(1)
public class JaminSecurityAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private JaminAuthenticationProvider jaminAuthenticationProvider;
private void disableDefaultSecurity(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
http.formLogin().disable();
http.logout().disable();
http.httpBasic().disable();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
this.disableDefaultSecurity(http);
http.antMatcher("/auth/check")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(new AuthFilter(), BasicAuthenticationFilter.class);
// http.authorizeRequests().anyRequest().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(jaminAuthenticationProvider);
}
}
Spring Security has some "before and after" steps. There are a few Handlers that can help. I don't know your code, but if you can get your authentication ok, maybe you just have to extend a SuccessHandler and set the authentication there, like i did in my blog project:
if(checkEmail(authentication)) {
val adminRole = SimpleGrantedAuthority("ROLE_ADMIN")
val oldAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities()
val updateAuthorities = mutableListOf<GrantedAuthority>()
updateAuthorities.add(adminRole)
updateAuthorities.addAll(oldAuthorities)
SecurityContextHolder.getContext().setAuthentication(UsernamePasswordAuthenticationToken(authentication.getPrincipal(),
authentication.getCredentials(),
updateAuthorities))
}
And about the filters, maybe you can find your answer here. I don't like using filters and interceptors, but sometimes they are really necessary.

Resources