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?
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.
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.
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.
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.
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; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With