Loading security for some specific tests only - spring-security

I'm trying to have some integration tests that exercise some business controllers, and other tests that exercise the security configuration.
So I have the business controllers tests extend a BaseTest class and the security configuration tests extend a SecurityBaseTest class.
I want to load the security configuration only for the tests that exercise it, and not for the business controllers tests as these should only exercise the controllers and not the security configuration.
#SpringBootTest(classes = { TestConfiguration.class, WebConfiguration.class })
#RunWith(SpringRunner.class)
public abstract class BaseTest {
#Before
public void setup() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(springSecurityFilterChain).build();
httpHeaders = new HttpHeaders();
}
}
#SpringBootTest(classes = { TestConfiguration.class, SecurityConfiguration.class, WebConfiguration.class })
public abstract class SecurityBaseTest extends BaseTest {
#Before
public void setup() throws Exception {
super.setup();
userFixtureService.addUserFixture();
addTokenToRequestHeader(httpHeaders, UserFixtureService.USER_EMAIL);
}
private void addTokenToRequestHeader(HttpHeaders headers, String username) {
tokenAuthenticationService.addTokenToResponseHeader(headers, username);
}
}
When running the business controllers tests using the command mvn clean install -Denv="test" -Ddb="h2" -Dtest=UserControllerTest -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 -Xnoagent -Djava.compiler=NONE" then the debugger shows that the security configuration is rightfully not being loaded.
But the testCrudOperations method fails as it returns a 403 status and not the 201 as expected. And the debugger shows that the UserController controller endpoint is not being hit at all.
public class UserControllerTest extends BaseTest {
#Test
public void testCrudOperations() throws Exception {
MvcResult mvcResult = this.mockMvc
.perform(post(RESTConstants.SLASH + UserDomainConstants.USERS)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)
.headers(httpHeaders)
.content(jacksonObjectMapper.writeValueAsString(userResource0)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.firstname").exists())
.andExpect(jsonPath("$.firstname").value(userResource0.getFirstname()))
.andExpect(jsonPath("$.lastname").value(userResource0.getLastname()))
.andExpect(jsonPath("$.email").value(userResource0.getEmail()))
.andExpect(header().string("Location", Matchers.containsString("/users/"))).andReturn();
UserResource retrievedUserResource = deserializeResource(mvcResult, UserResource.class);
assertThatUserResource(retrievedUserResource).hasEmail(userResource0.getEmail());
assertThatUserResource(retrievedUserResource)
.hasRole(retrievedUserResource.getUserRoles().iterator().next().getRole());
userResource0.setResourceId(retrievedUserResource.getResourceId());
}
}
When running the security configuration tests using the command mvn clean install -Denv="test" -Ddb="h2" -Dtest=UserAuthenticationTest -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 -Xnoagent -Djava.compiler=NONE" then the debugger shows that the security configuration is rightfully being loaded.
But the testUnsecuredResourceGrantsAccess method fails, with the error No qualifying bean of type 'org.springframework.security.web.FilterChainProxy' available message.
public class UserAuthenticationTest extends SecurityBaseTest {
#Test
public void testUnsecuredResourceGrantsAccess() throws Exception {
this.mockMvc.perform(
post(RESTConstants.SLASH + UserDomainConstants.USERS + RESTConstants.SLASH + UserDomainConstants.LOGIN)
.accept(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isBadRequest())
.andReturn();
}
}
The security configuration is:
#EnableWebSecurity(debug = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name()));
authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromCredentialsFilter;
}
#Bean
public AuthenticationFromTokenFilter authenticationFromTokenFilter() throws Exception {
AuthenticationFromTokenFilter authenticationFromTokenFilter = new AuthenticationFromTokenFilter(new NegatedRequestMatcher(new AntPathRequestMatcher("/users/login")));
authenticationFromTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromTokenFilter;
}
#Bean
FilterRegistrationBean<AuthenticationFromTokenFilter> disableAutoRegistration(final AuthenticationFromTokenFilter filter) {
final FilterRegistrationBean<AuthenticationFromTokenFilter> registration = new FilterRegistrationBean<AuthenticationFromTokenFilter>(filter);
registration.setEnabled(false);
return registration;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(simpleCORSFilter, ChannelProcessingFilter.class);
http
.addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/error").permitAll()
.antMatchers("/users/login").permitAll()
.antMatchers("/admin/**").hasRole(UserDomainConstants.ROLE_ADMIN)
.anyRequest().authenticated();
}
}

Related

JUnit5 test a secured controller in using Spring security

I have a simple controller that should only be accessible in a secured context.
In my application, I only whitelist other URLs, not /user.
In the unit test for the single #GetResponse I get an Http Status of 401 instead of a 200. I expect this, as it is secured.
When I actually run the application with a logged in user, I get a 200, and a valid result.
How can I Unit Test the behaviour of the request and any other secured controller paths?
Unit test:
#SpringBootTest
#AutoConfigureMockMvc
class UserControllerTest {
#InjectMocks
UserController userController;
#Autowired
private MockMvc mockMvc;
#Autowired
private ApplicationContext applicationContext;
#Test
void getUserNone() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get("/user"))
.andExpect(status().isOk());
}
}
App:
#SpringBootApplication
public class WebApp extends WebSecurityConfigurerAdapter {
public static void main(final String... args) {
SpringApplication.run(WebApp.class, args);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests(a -> a
.antMatchers("/error", "/css/**", "/webjars/**").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(e -> e
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
)
.oauth2Login();
}
}
Controller:
#Controller
public class UserController {
#GetMapping("/user")
public #ResponseBody
Map<String, Object> getUser(#AuthenticationPrincipal final OAuth2User principal) {
if (principal == null) {
return Collections.EMPTY_MAP;
} else {
return Collections.singletonMap("name", principal.getName());
}
}
}
Obviously, I could add /user to the whitelisted paths, but that would be incorrect for any secured parts of the application.
You can use the annotation #WithMockUser and #WithAnonymousUser which creates a fake Authentication that is populated in SecurityContext.
Documentation can be found here .
Example code:
#Test
#WithMockUser("john")
void testAuthorize() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get("/user"))
.andExpect(status().isOk());
}
#Test
#WithAnonymousUser
void testUnauthorized() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.get("/user"))
.andExpect(status().isUnauthorized());
}

How to config ip address dynamically with spring-security?

I'm new to spring-security, I need to setup a authorization system to secure a REST service.
In my case, my "users" are some servers of different departments and companies.So I tried to config the servers as MyUser which is sub class of UserDetails.
But I got an issue when I was asked to authorize the ip address of the servers. I saw there're ip address authorizations in WebSecurityConfigurerAdapter.configure(HttpSecurity http), and I can retrieve the configuration with configure(AuthenticationManagerBuilder auth){auth.userDetailsService(myUserDetailsService);}. But it seems that the configure(HttpSecurity http) method only runs once when the system boots.
So, what should I do? Is there any way to add a customized checker or something to verify the ip address?
These are my code:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.httpBasic().and()
.authorizeRequests()
.antMatchers("/order/**").hasAuthority("read_order") //(1)
.antMatchers("/order/**").hasIpAddress("192.168.1.45") //(2)
.anyRequest().denyAll();
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
}
With customized userService which implements the UserDetailsService, I can replace the (1) line with configuration in database. Which means when I change the configuration, Spring Security will load it from database. I don't need to restart the system.
How do I do the similar thing to the (2) line?
I found a solution. I'm not sure is it the best way, but it can work. This is the solution.
First, we can define a class which implements AccessDecisionManager:
#Service
public class ResourceAccessDecisionManager implements AccessDecisionManager {
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws
AccessDeniedException, InsufficientAuthenticationException {
//...
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
String ip = request.getRemoteHost();
Object principal = authentication.getPrincipal();
User user;
if (principal instanceof User){
user=(User)principal;
if (!ip.equals(user.getIpConfig())){
throw new AccessDeniedException("wrong ip");
}
}
//...
}
//...
}
Surely, you had to store the ip config within the User class.
Then, we define a class extends AbstractSecurityInterceptor implements Filter:
#Service
public class ResourceFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
//...
#Autowired
public void setMyAccessDecisionManager(ResourceAccessDecisionManager resourceAccessDecisionManager) {
super.setAccessDecisionManager(resourceAccessDecisionManager);
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
invoke(fi);
}
private void invoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
//...
}
And add the filter into security config class:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final ResourceService resourceService;
private final UserService userService;
private final HttpServletRequest request;
private final ResourceFilterSecurityInterceptor resourceFilterSecurityInterceptor;
private final EnvironmentConfig environmentConfig;
#Autowired
public SecurityConfiguration(ResourceService resourceService, UserService userService,
HttpServletRequest request,
ResourceFilterSecurityInterceptor resourceFilterSecurityInterceptor,
EnvironmentConfig environmentConfig) {
this.resourceService = resourceService;
this.userService = userService;
this.request = request;
this.resourceFilterSecurityInterceptor = resourceFilterSecurityInterceptor;
this.environmentConfig = environmentConfig;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.httpBasic().and()
.authorizeRequests()
.antMatchers("/order/**").authenticated()
.anyRequest().authenticated()
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
http.addFilterBefore(resourceFilterSecurityInterceptor,FilterSecurityInterceptor.class);
// #formatter:on
}
//...
}
Now it can work, but any suggestion is welcome and admirable to improve the solution.

How to secure Apache Camel rest endpoint with Spring Security and OAuth2

I'm working on Spring Boot application with configured SSO/OAuth2 security.
Authentication works fine for my rest controllers and now I need to secure my Apache Camel route with a rest endpoint.
As I understand there are several ways how to do it:
By adding auth processor to my route
By adding policy (SpringSecurityAuthorizationPolicy) to my route
By handlers option to jetty endpoint
I'm trying to do it by adding new auth processor to my rest endpoint but I stuck on this exception:
org.springframework.security.oauth2.common.exceptions.OAuth2Exception:
No AuthenticationProvider found for
org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
During debugging I see that org.springframework.security.authentication.ProviderManager.getProviders() contains only one provider AnonymousAuthenticationProvider so probably I have to register appropriate provider...
Can someone help me to find the right way to solve this problem please?
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Value("${oauth2.token.endpoint}")
private String tokenEndpoint;
#Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId("clientId");
tokenServices.setClientSecret("clientSecret");
tokenServices.setCheckTokenEndpointUrl(tokenEndpoint);
return tokenServices;
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
}
#Configuration
public class EmbeddedServerRoute {
#Bean
public RoutesBuilder embeddedServer() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
restConfiguration().component("jetty").port("8081").bindingMode(RestBindingMode.json);
}
};
}
}
#Component
public class RestTestRoute extends RouteBuilder {
#Autowired
private AuthProcessor authProcessor;
#Override
public void configure() throws Exception {
from("rest:get:/test").process(authProcessor).to("mock:end").end();
}
}
#Component
public class AuthProcessor implements Processor {
#Autowired
private AuthenticationManager authenticationManager;
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new OAuth2AuthenticationDetailsSource();
#Override
public void process(Exchange exchange) throws Exception {
HttpServletRequest request = exchange.getIn().getBody(HttpServletRequest.class);
Subject subject = new Subject();
Authentication auth = getAuth(request);
subject.getPrincipals().add(auth);
exchange.getIn().setHeader(Exchange.AUTHENTICATION, subject);
}
private Authentication getAuth(HttpServletRequest request) throws OAuth2Exception {
Authentication authentication = null;
try {
authentication = tokenExtractor.extract(request);
if (authentication != null) {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
return authenticationManager.authenticate(authentication);
}
} catch (Exception e) {
throw new OAuth2Exception(e.getMessage());
}
throw new OAuth2Exception("Not Authorized to view resource");
}
}
As a final solution I decided to use Spring Boot embedded servlet container instead of Apache Camel rest component. So it could be easily secured by Spring Security. This could be done by creating additional beans:
#Bean
public ServletRegistrationBean servletRegistrationBean() {
SpringServerServlet serverServlet = new SpringServerServlet();
ServletRegistrationBean regBean = new ServletRegistrationBean(serverServlet, "/camel/*");
Map<String, String> params = new HashMap<>();
params.put("org.restlet.component", "restletComponent");
regBean.setInitParameters(params);
return regBean;
}
#Bean
public Component restletComponent() {
return new Component();
}
#Bean
public RestletComponent restletComponentService() {
return new RestletComponent(restletComponent());
}

Unable to set a custom authentication failure handler in 4.0 spring security

I want to set up custom AuthenticationFailureHandler in my project. Even though I configure my authenticationFailureHandler as below , it is not properly picked up when the sign-in fails.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
..................
#Inject
private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;
..................
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated().and()
.formLogin().loginPage("/signin").failureHandler(ajaxAuthenticationFailureHandler)
.permitAll()
.failureUrl("/signin")
.defaultSuccessUrl("/search").and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/signout"))
.logoutSuccessUrl("/signin")
.permitAll().and().csrf();
}
My custom AuthenticationFailureHandler Class
#Component
public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// Custom code
}
}
When I debug the application, the method onAuthenticationFailure is called from SimpleUrlAuthenticationFailureHandler, but not in my custom AjaxAuthenticationFailureHandler even though I extend the same SimpleUrlAuthenticationFailureHandler.
What could be the mistake or any missed configuration to resolve the problem?
In this case, when we set the custom AjaxAuthenticationFailureHandler and then we configure failureUrl("/signin"),the configuration failureUrl("/signin") will overwrite the already configured AjaxAuthenticationFailureHandler with a new SimpleUrlAuthenticationFailureHandler.
This is the implementation of failureUrl and failureHandler methods as in AbstractAuthenticationFilterConfigurer.
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter> extends AbstractHttpConfigurer<T, B> {
...........
private AuthenticationFailureHandler failureHandler;
private String failureUrl;
..........
public final T failureUrl(String authenticationFailureUrl) {
T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));
this.failureUrl = authenticationFailureUrl;
return result;
}
public final T failureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.failureUrl = null;
this.failureHandler = authenticationFailureHandler;
return getSelf();
}
}
If we set only the failurehandler as below, our custom AjaxAuthenticationFailureHandler will be set.
.formLogin().loginPage("/signin")
.permitAll()
.failureHandler(ajaxAuthenticationFailureHandler)
.defaultSuccessUrl("/search").and()
If we want to set the failure URL we can set it in the custom AjaxAuthenticationFailureHandler using setDefaultFailureUrl(String defaultFailureUrl) which is derived from it's parent class SimpleUrlAuthenticationFailureHandler.
You can create a constructor in your AjaxAuthenticationFailureHandler class and pass in the defaultFailureUrl parameter down to the parent clss you extend (SimpleUrlAuthenticationFailureHandler). Your AjaxAuthenticationFailureHandler class would look like: -
#Component
public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
public AjaxAuthenticationFailureHandler (String defaultFailureUrl) {
super(defaultFailureUrl);
}
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// Custom code
}
}
Your configure block would then look like this: -
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/signin")
.failureHandler(new AjaxAuthenticationFailureHandler("/signin?auth=failure") // or whatever is a sensible url
.permitAll()
.defaultSuccessUrl("/search").and()
...
Note, it's really important that the "/signin?auth=failure" is added to an authorizeRequests() section otherwise the controller won't pick up the auth parameter e.g.
.and()
.authorizeRequests()
.antMatchers(
"/css/**",
"/js/**",
"/images/**",
"/signin**" // REALLY IMPORTANT !!!
).permitAll()
See https://stackoverflow.com/a/39618113/1692179 for more information on that.
Your controller can now check for the auth parameter e.g.
#Controller
public class SigninController
#RequestMapping(value="/login", method = RequestMethod.GET)
public String handleError (Model model,
#RequestParam(name = "auth", required = false) String auth) {
if ("error".equals(auth)) {
model.addAttribute("error", "invalid username/password");
}
return "login";
}
}
Hope this helps! :-)

Spring Security with Java Configuration: How to handle BadCredentialsException from a custom provider

I need to authenticate some rest services using a token id in the url (or maybe in the request header - but this is not important for now). I am trying to use java configuration to set this up using as a guide this post. My problem is that I do not know how to handle "BadCredentialsException" that is thrown when the authentication fails from the provider. Here is my Security Config:
public static class SecurityConfigForRS extends
WebSecurityConfigurerAdapter {
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.regexMatcher("^/rest.*")
.addFilterBefore(
new TokenAuthenticationFilter(
authenticationManagerBean()),
AbstractPreAuthenticatedProcessingFilter.class)
.and().csrf().disable();
}
}
For now I skip the other implementations - if it helps I will post them later.
When the token is missing or is invalid, the TokenAuthernticationProvider throws a BadCredentialsException. I need to catch this and send back an 401-Unauthorized. Is it possible to do this?
The first Filter I created was a subclass of GenericFilterBean and it did not have support for authentication failure handler or success handler. However AbstractAuthenticationProcessingFilter supports success and failure handlers. My filter is as simple as that:
public class TokenAuthenticationProcessingFilter extends
AbstractAuthenticationProcessingFilter {
public TokenAuthenticationProcessingFilter(
RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException,
IOException, ServletException {
Authentication auth = new TokenAuthentication("-1");
try {
Map<String, String[]> params = request.getParameterMap();
if (!params.isEmpty() && params.containsKey("auth_token")) {
String token = params.get("auth_token")[0];
if (token != null) {
auth = new TokenAuthentication(token);
}
}
return this.getAuthenticationManager().authenticate(auth);
} catch (AuthenticationException ae) {
unsuccessfulAuthentication(request, response, ae);
}
return auth;
}}
and my http security is:
public static class SecurityConfigForRS extends
WebSecurityConfigurerAdapter {
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Bean
protected AbstractAuthenticationProcessingFilter getTokenAuthFilter()
throws Exception {
TokenAuthenticationProcessingFilter tapf = new TokenAuthenticationProcessingFilter(
new RegexRequestMatcher("^/rest.*", null));
tapf.setAuthenticationManager(authenticationManagerBean());
return tapf;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.regexMatcher("^/rest.*")
.addFilterAfter(getTokenAuthFilter(),
BasicAuthenticationFilter.class).csrf().disable();
}
}
The filter chain order does matter! I placed it after BasicAuthenticationFilter and it works fine. Of course there might be a better solution but for now this works!
May be you can try with Global Exception handling with ControllerAdvice
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler // when Invalid Credentials
public ResponseEntity<ErrorMessage> handleInvalidCredentialsException(
BadCredentialsException ex) {
return new ResponseEntity<ErrorMessage>(
new ErrorMessage(ex.getMessage()), HttpStatus.UNAUTHORIZED);
}
#Getter
#Setter
#AllArgsConstructor
class ErrorMessage {
private String error;
}
}

Resources