Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security custom authentication filter using Java Config

I'm trying to configure Spring Security using Java config in a basic web application to authenticate against an external web service using an encrypted token provided in a URL request parameter.

I would like (I think) to have a security filter that intercepts requests from the Login Portal (they all go to /authenticate), the filter will use an AuthenticationProvider to process the bussiness logic of the authentication process.

Login Portal --> Redirect '\authenticate' (+ Token) --> Authenticate Token back to Login Portal (WS) --> If success get roles and setup user.

I have created a filter..

@Component public final class OEWebTokenFilter extends GenericFilterBean {     @Override     public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {         if (request instanceof HttpServletRequest) {              OEToken token = extractToken(request);              // dump token into security context (for authentication-provider to pick up)              SecurityContextHolder.getContext().setAuthentication(token);         }     }        chain.doFilter(request, response); } 

An AuthenticationProvider...

@Component public final class OEWebTokenAuthenticationProvider implements AuthenticationProvider {     @Autowired     private WebTokenService webTokenService;      @Override     public boolean supports(final Class<?> authentication) {         return OEWebToken.class.isAssignableFrom(authentication);     }      @Override     public Authentication authenticate(final Authentication authentication) {          if (!(authentication instanceof OEWebToken)) {              throw new AuthenticationServiceException("expecting a OEWebToken, got " + authentication);         }          try {             // validate token locally             OEWebToken token = (OEWebToken) authentication;             checkAccessToken(token);              // validate token remotely             webTokenService.validateToken(token);              // obtain user info from the token             User userFromToken = webTokenService.obtainUserInfo(token);              // obtain the user from the db             User userFromDB = userDao.findByUserName(userFromToken.getUsername());              // validate the user status             checkUserStatus(userFromDB);              // update ncss db with values from OE             updateUserInDb(userFromToken, userFromDB);              // determine access rights             List<GrantedAuthority> roles = determineRoles(userFromDB);              // put account into security context (for controllers to use)             return new AuthenticatedAccount(userFromDB, roles);         } catch (AuthenticationException e) {             throw e;         } catch (Exception e) {              // stop non-AuthenticationExceptions. otherwise full stacktraces returned to the requester              throw new AuthenticationServiceException("Internal error occurred");         }     } 

And my Spring Security Config

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {      @Autowired     OESettings oeSettings;      @Bean(name="oeAuthenticationService")     public AuthenticationService oeAuthenticationService() throws AuthenticationServiceException {         return new AuthenticationServiceImpl(new OEAuthenticationServiceImpl(), oeSettings.getAuthenticateUrl(), oeSettings.getApplicationKey());     }      @Autowired     private OEWebTokenFilter tokenFilter;      @Autowired     private OEWebTokenAuthenticationProvider tokenAuthenticationProvider;      @Autowired     private OEWebTokenEntryPoint tokenEntryPoint;      @Bean(name="authenticationManager")     @Override     public AuthenticationManager authenticationManagerBean() throws Exception {         return super.authenticationManagerBean();     }      @Override     public void configure(AuthenticationManagerBuilder auth)  throws Exception {         auth.authenticationProvider(tokenAuthenticationProvider);     }      @Bean     public FilterRegistrationBean filterRegistrationBean () {           FilterRegistrationBean registrationBean = new FilterRegistrationBean();              registrationBean.setFilter(tokenFilter);             registrationBean.setEnabled(false);         return registrationBean;     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http.csrf().disable()             .authorizeRequests()             .antMatchers("/authenticate**").permitAll()             .antMatchers("/resources/**").hasAuthority("ROLE_USER")             .antMatchers("/home**").hasAuthority("ROLE_USER")             .antMatchers("/personSearch**").hasAuthority("ROLE_ADMIN")             // Spring Boot actuator endpoints             .antMatchers("/autoconfig**").hasAuthority("ROLE_ADMIN")             .antMatchers("/beans**").hasAuthority("ROLE_ADMIN")             .antMatchers("/configprops**").hasAuthority("ROLE_ADMIN")             .antMatchers("/dump**").hasAuthority("ROLE_ADMIN")             .antMatchers("/env**").hasAuthority("ROLE_ADMIN")             .antMatchers("/health**").hasAuthority("ROLE_ADMIN")             .antMatchers("/info**").hasAuthority("ROLE_ADMIN")             .antMatchers("/mappings**").hasAuthority("ROLE_ADMIN")             .antMatchers("/metrics**").hasAuthority("ROLE_ADMIN")             .antMatchers("/trace**").hasAuthority("ROLE_ADMIN")             .and()                 .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)                 .authenticationProvider(tokenAuthenticationProvider)                 .antMatcher("/authenticate/**")                 .exceptionHandling().authenticationEntryPoint(tokenEntryPoint)             .and()                 .logout().logoutSuccessUrl(oeSettings.getUrl());     } } 

My problem is the configuration of the filter in my SpringConfig class. I want the filter to only come into effect when the request is for the /authenticate URL, I've added .antMatcher("/authenticate/**") to the filter configuration.

.and()                 .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)                 .authenticationProvider(tokenAuthenticationProvider)                 .antMatcher("/authenticate/**")                 .exceptionHandling().authenticationEntryPoint(tokenEntryPoint) 

When I have this line in all other URLs are no longer secured, I can manually navigate to /home without authenticating, remove the line and /home is authenticated.

Should I be declaring a filter that is only applicable to a specific URL?

How can I implement this whilst maintaining the security of other URLs?

like image 714
Steve Faro Avatar asked Dec 16 '14 15:12

Steve Faro


People also ask

How do you make a Spring Security filter?

There are a couple of possible methods: addFilterBefore(filter, class) adds a filter before the position of the specified filter class. addFilterAfter(filter, class) adds a filter after the position of the specified filter class.

How do I register a custom filter in Spring boot?

There are three ways to add your filter, Annotate your filter with one of the Spring stereotypes such as @Component. Register a @Bean with Filter type in Spring @Configuration. Register a @Bean with FilterRegistrationBean type in Spring @Configuration.

What is authentication filter in Spring Security?

Class AuthenticationFilterA Filter that performs authentication of a particular request. An outline of the logic: A request comes in and if it does not match setRequestMatcher(RequestMatcher) , then this filter does nothing and the FilterChain is continued.

How do I set up Spring Security authentication?

The Spring Security Configuration Here we're using the httpBasic() element to define Basic Authentication inside the SecurityFilterChain bean. What's relevant here is the <http-basic> element inside the main <http> element of the configuration. This is enough to enable Basic Authentication for the entire application.


1 Answers

I've resolved my issue by performing a check on the authentication status in the filter before involking the authentication provider....

Config

.and()     .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)     .authenticationProvider(tokenAuthenticationProvider)     .exceptionHandling().authenticationEntryPoint(tokenEntryPoint) 

Filter

@Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)         throws IOException, ServletException {      logger.debug(this + "received authentication request from " + request.getRemoteHost() + " to " + request.getLocalName());      if (request instanceof HttpServletRequest) {         if (isAuthenticationRequired()) {             // extract token from header             OEWebToken token = extractToken(request);              // dump token into security context (for authentication-provider to pick up)             SecurityContextHolder.getContext().setAuthentication(token);         } else {             logger.debug("session already contained valid Authentication - not checking again");         }     }      chain.doFilter(request, response); }      private boolean isAuthenticationRequired() {     // apparently filters have to check this themselves.  So make sure they have a proper AuthenticatedAccount in their session.     Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();     if ((existingAuth == null) || !existingAuth.isAuthenticated()) {         return true;     }      if (!(existingAuth instanceof AuthenticatedAccount)) {         return true;     }      // current session already authenticated     return false; } 
like image 58
Steve Faro Avatar answered Sep 30 '22 09:09

Steve Faro