Spring Boot Mock MVC applying filter to wrong url pattern - spring-boot

I'm adding an admin filter to a specific URL like this
#Bean
public FilterRegistrationBean<AdminFilter> adminFilterRegistrationBean() {
FilterRegistrationBean<AdminFilter> registrationBean = new FilterRegistrationBean<>();
AdminFilter adminFilter = new AdminFilter();
registrationBean.setFilter(adminFilter);
registrationBean.addUrlPatterns("/api/user/activate");
registrationBean.addUrlPatterns("/api/user/deactivate");
registrationBean.setOrder(Integer.MAX_VALUE);
return registrationBean;
}
While I'm testing it with postman or in browser, the filter is applied correctly, only applied to those URL pattern.
But, when I write test for it, somehow the filter is applied to another URL too.
this.mockMvc.perform(
get("/api/issue/").header("Authorization", defaultToken)
).andDo(print()).andExpect(status().isOk())
.andExpect(content().json("{}"));
This code return an error with code "403", on the log it says because the user is not an admin, which means the admin filter applied to "/api/issue/" URL on the mock mvc request.
I'm using #AutoConfigureMockMvc with #Autowired to instantiate the mockMVC.
anyone know why it's happening?
Full code of the admin filter:
#Component
public class AdminFilter extends GenericFilterBean {
UserService userService;
#Override
public void doFilter(
ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain
) throws IOException, ServletException {
if (userService == null){
ServletContext servletContext = servletRequest.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
userService = webApplicationContext.getBean(UserService.class);
}
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
UUID userId = UUID.fromString((String)httpRequest.getAttribute("userId"));
User user = userService.fetchUserById(userId);
if (!user.getIsAdmin()) {
httpResponse.sendError(HttpStatus.FORBIDDEN.value(), "User is not an admin");
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
Full code of the test file:
#SpringBootTest()
#AutoConfigureMockMvc
#Transactional
public class RepositoryIntegrationTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private RepositoryRepository repositoryRepository;
#Autowired
private UserRepository userRepository;
private String defaultToken;
private String otherToken;
#BeforeEach
void init() {
User defaultUser = userRepository.save(new User("username", "email#mail.com", "password"));
System.out.println(defaultUser);
User otherUser = userRepository.save(new User("other", "other#mail.com", "password"));
defaultToken = "Bearer " + generateJWTToken(defaultUser);
otherToken = "Bearer " + generateJWTToken(otherUser);
}
private String generateJWTToken(User user) {
long timestamp = System.currentTimeMillis();
return Jwts.builder().signWith(SignatureAlgorithm.HS256, Constants.API_SECRET_KEY)
.setIssuedAt(new Date(timestamp))
.setExpiration(new Date(timestamp + Constants.TOKEN_VALIDITY))
.claim("userId", user.getId())
.compact();
}
#Test
public void shouldReturnAllRepositoriesAvailableToUser() throws Exception {
this.mockMvc.perform(
get("/api/issue/").header("Authorization", defaultToken)
).andExpect(status().isOk())
.andExpect(content().json("{}"));
}
}

Your AdminFilter is being registered twice. Once through the FilterRegistrationBean and once due to the fact that it is an #Component and thus detected by component scanning.
To fix do one of 2 things
Remove #Component
Re-use the automatically created instance for the FilterRegistrationBean.
Removing #Component is easy enough, just remove it from the class.
For option 2 you can inject the automatically configured filter into the FilterRegistrationBean configuration method, instead of creating it yourself.
#Bean
public FilterRegistrationBean<AdminFilter> adminFilterRegistrationBean(AdminFilter adminFilter) {
FilterRegistrationBean<AdminFilter> registrationBean = new FilterRegistrationBean<>(adminFilter);
registrationBean.addUrlPatterns("/api/user/activate");
registrationBean.addUrlPatterns("/api/user/deactivate");
registrationBean.setOrder(Integer.MAX_VALUE);
return registrationBean;
}
An added advantage of this is that you can use autowiring to set up dependencies instead of doing lookups in the init method. I would also suggest using the OncePerRequestFilter. This would clean up your filter considerably.
#Component
public class AdminFilter extends OncePerRequestFilter {
private final UserService userService;
public AdminFilter(UserService userService) {
this.userService=userService;
}
#Override
protected void doFilter(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain filterChain) throws IOException, ServletException {
UUID userId = UUID.fromString((String)httpRequest.getAttribute("userId"));
User user = userService.fetchUserById(userId);
if (!user.getIsAdmin()) {
httpResponse.sendError(HttpStatus.FORBIDDEN.value(), "User is not an admin");
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}

Related

How to Mock with MockHttpSession and Junit

Looked at this post where #slim gave a solution close to what I am asking about. I am trying to write unit test on the class below. I am pulling out the sessionId. (look for String sessId inside doFilterInternal method)
#Component
public class AppLoggingFilter extends OncePerRequestFilter {
private AppLoggingMDCService mdcService;
#Inject
public AppLoggingFilter(AppLoggingMDCService appLoggingMDCService) {
Assert.notNull(appLoggingMDCService, "AppLoggingMDCService must not be null");
this.mdcService = appLoggingMDCService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Principal principal = request.getUserPrincipal();
String sessId = DigestUtils.sha256Hex(request.getSession().getId());
if (principal != null) {
String userId = principal.getName();
mdcService.put(AppLoggingMDCService.LOG_KEY_USER_ID, userId);
}
mdcService.put(AppLoggingMDCService.LOG_KEY_SESSION_ID, sessId);
try {
filterChain.doFilter(request, response);
} finally {
mdcService.removeAll();
}
}
Naturally the test below is failing because I don't have a valid session. Obviously I am getting a null-pointer-exception whenever i call filter.doFilterInternal(request, response, filterChain);. In the test class, the "mock-session" is not set up and has no ID. In my unit test I have this.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles(value = {"test"})
public class AppLoggingFilterUnitTest {
#Mock
AppLoggingMDCService mdcService;
#Mock
MockHttpServletRequest request;
#Mock
MockHttpServletResponse response;
#Mock
MockFilterChain filterChain;
#Mock
MockHttpSession session;
#InjectMocks
AppLoggingFilter filter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
request.setSession(session);
}
#Test
public void testCustomerIdHasBeenLogged() throws Exception {
String customerId = "1234";
when(request.getHeader(AuthorizationConstants.CUSTOMER_KEY)).thenReturn(customerId);
filter.doFilterInternal(request, response, filterChain);
verify(mdcService, times(1)).put(AppLoggingMDCService.LOG_KEY_CUST_ID,customerId);
}
}
So coming back to my question, how do simulate as valid mocked "MockHttpSession" so my others tests don't fail?
UPDATE
So I added the session into my test class like this. In my setup method is where I said to return the "Mocked" session. The test passes only when I call
String sessId = request.getSession().getId();. if I try to do the DigestUtils.sha256Hex like
String sessId = DigestUtils.sha256Hex(request.getSession().getId()); ,all tests fail due to a null-pointer. I am not sure why. Mocking DigestUtils doesn't make much sense.
#Mock
AppLoggingMDCService mdcService;
#Mock
HttpServletRequest request;
#Mock
HttpServletResponse response;
#Mock
FilterChain filterChain;
#Mock
HttpSession session;
#InjectMocks
AppLoggingFilter filter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(request.getSession()).thenReturn(this.session);
}
You are doing to much in your test and it is way to complex. Remove the runner, remove Mockito for the Mock* as those need to be instantiated not mocked.
public class AppLoggingFilterUnitTest {
#Mock
private AppLoggingMDCService mdcService;
#InjectMocks
private AppLoggingFilter filter;
private MockHttpServletRequest request;
private MockHttpServletResponse response = new MockHttpServletResponse();
private MockFilterChain filterChain = new MockFilterChain();
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.request = new MockHttpServletRequest();
this.request.addHeader(AuthorizationConstants.CUSTOMER_KEY, "1234");
}
#Test
public void testCustomerIdHasBeenLogged() throws Exception {
filter.doFilterInternal(request, response, filterChain);
verify(mdcService, times(1)).put(AppLoggingMDCService.LOG_KEY_CUST_ID,customerId);
}
}
With the creating of the mocks for the Mock* classes you basically defeated the purpose of those classes.

Adding custom header to response in spring rest / spring boot

i am trying to send session id in response header in rest controller but excludepathpattern() seems not working
** the configuration class is not triggering **
i have tried changing the sevlet version but it didnt work
ContextListener
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
Map<String, HttpSession> map = new HashMap<>();
context.setAttribute("activeUsers", map);
HttpSessionListener
ServletContext context = session.getServletContext();
Map<String, HttpSession> activeUsers = (Map<String, HttpSession>) context.getAttribute("activeUsers");
activeUsers.put(session.getId(), session);
HandlerInterceptor
ServletContext context = request.getServletContext();
Map<String, HttpSession> activeUsers = (Map<String, HttpSession>) context.getAttribute("activeUsers");
String sessionId = request.getHeader("sessionId");
String requestUrl = request.getRequestURL().toString();
if (requestUrl.contains("/getOtp") || requestUrl.contains("/validateOtp")) {
return true;
} else {
if (activeUsers.containsKey(sessionId)) {
return true;
} else {
response.setStatus(401);
return false;
}
}
interceptorconfigurartion by extendig websecurityconfigure
#Configuration
#EnableAutoConfiguration
public class SessionInterceptorConfig implements WebMvcConfigurer {
#Autowired
private SessionHanlderInterceptor sessionHandlerIntercepto;
#Override
public void addInterceptors(InterceptorRegistry registry) {
// List<String> paths = new ArrayList<String>();
// paths.add("/auth/*");
registry.addInterceptor(sessionHandlerIntercepto).excludePathPatterns("/auth/**");
}
#Bean
public ServletListenerRegistrationBean<CustomSessionListener> filterRegistrationBean() {
ServletListenerRegistrationBean<CustomSessionListener> registrationBean = new ServletListenerRegistrationBean<CustomSessionListener>();
CustomSessionListener customURLFilter = new CustomSessionListener();
registrationBean.setListener(customURLFilter);
registrationBean.setOrder(1); // set precedence
return registrationBean;
}
#Bean
public ServletListenerRegistrationBean<CustomServletContextListener> filterContextRregistration() {
ServletListenerRegistrationBean<CustomServletContextListener> registrationBean = new ServletListenerRegistrationBean<CustomServletContextListener>();
CustomServletContextListener customURLFilter = new CustomServletContextListener();
registrationBean.setListener(customURLFilter);
registrationBean.setOrder(1); // set precedence
return registrationBean;
}
Sprinboot main class
#SpringBootApplication
public class CustomerApplication extends SpringBootServletInitializer {
i expect to add the session id to header in response and to check for the sessionid in request
You can use spring web component "OncePerRequestFilter". You need to inject a bean which extends OncePerRequestFilter. Example:
public class CustomHeaderFilter extends OncePerRequestFilter {
#Override
public void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain) throws IOException, ServletException {
response.setHeader(customHeaderName, customHeaderValue);
chain.doFilter(request, response);
}
}

Springboot Jersey - Custom Filter with ServletProperties.FILTER_STATIC_CONTENT_REGEX

I have a Springboot Application running with a Jersey config line property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "/mywebpage/.*") to enable rendering static html/js/css/image... content for that specific path prefix. All is fine and mywebpage is loading perfectly on /mywebpage/index.html.
Now after running mywebpage for a few months, we want to redirect a percentage of users (those enabled for a beta) to a new webpage (say https://stackoverflow.com/). So I'm trying to write a Filter for the path /mywebpage/index.html to redirect the user to the new page if they are enabled for the beta. Now the part that stumped me, is that for some reason, no filters are being invoked for any calls to /mywebpage/.* (calls made by the browser to get the html/js/css/... content) (checked with breakpoints and dummy logs). I assume the suspect would be the property ServletProperties.FILTER_STATIC_CONTENT_REGEX.
I already have the bit to compute whether the user is beta enabled, and just need to fit it in the Filter.
Here's what I was trying:
#Provider
public class RedirectionFilter implements ContainerRequestFilter {
#Autowired
private BetaAudienceService betaAudienceService;
#Context
private UriInfo info;
private static final Logger log = LogManager.getLogger(getClass());
#Override
public void filter(ContainerRequestContext request) throws IOException {
log.info("Test Log :: Path=" + request.getUriInfo().getAbsolutePath().getPath());
if (request.getUriInfo().getAbsolutePath().getPath().equals("/mywebpage/index.html") && isBetaEnabled(request)) {
try {
request.abortWith(Response.temporaryRedirect(new URI("https://stackoverflow.com/")).build());
} catch (URISyntaxException e) {
throw new IOException("Failed to build or respond with Redirection URI", e);
}
}
}
private boolean isBetaEnabled(ContainerRequestContext request) { ... }
}
and
public class JerseyConfig extends ResourceConfig {
#Autowired
private ApplicationContext appCtx;
#PostConstruct
public void setup() {
register(A.class);
register(B.class);
...
}
public JerseyConfig() {
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, Constants.STATIC_CONTENT_PATH);
}
}
Any suggestions on how I can get around this? Or probably is my approach wrong altogether?
I was able to figure this out.
In this case, Jersey is set to run as a Filter (since we need to use ServletProperties.FILTER_STATIC_CONTENT_REGEX) and Jersey handles all the service requests except for the specific paths /mywebpage/* where Jersey is configured to ignore the request due to the static content filter property. Hence those ignored/filtered requests are handled by Springboot directly, which means we can just use a Springboot filter.
Here's the code for the Filter:
public class RedirectionFilter extends OncePerRequestFilter /* can also just `implements Filter` instead */ {
public static final String REDIRECT_PATH = "/mywebpage/index.html";
private static final Logger log = LogManager.getLogger(RedirectionFilter.class);
private final MyConfig myConfig;
public UnifiedShellRedirectionFilter(MyConfig myConfig) {
this.myConfig = myConfig;
}
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("Test Log :: Path: " + request.getPathInfo());
if (isBetaEnabled(request)) {
response.sendRedirect(myConfig.getRedirectionEndpoint() /* "https://stackoverflow.com/" */);
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
log.info("Destructing Filter: " + this.getClass().getSimpleName());
}
private boolean isBetaEnabled(HttpServletRequest request) { ... }
}
And put the following in your #Configuration class:
#Bean
// #Autowired
public FilterRegistrationBean registerRedirectionFilter(MyConfig myConfig) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setName(RedirectionFilter.class.getSimpleName());
registrationBean.setFilter(new UnifiedShellRedirectionFilter(myConfig));
registrationBean.addUrlPatterns(RedirectionFilter.REDIRECT_PATH);
return registrationBean;
}

Propagating correlation-id not working

I have the problem that the correlation-id is not propagated from my first to the my second microservice. I started to implement a servlet filter, a context and a context-holder as follows:
#Component
// Do not name bean "RequestContextFilter", otherwise filter will not work!
public class CallContextFilter implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;CallContextHolder.getContext().setCorrelationId(httpServletRequest.getHeader(CallContext.CORRELATION_ID));
filterChain.doFilter(httpServletRequest, servletResponse);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
#Component
#Getter
#Setter
public class CallContext {
public static final String CORRELATION_ID = "correlation-id";
private String correlationId = new String();
}
public class CallContextHolder {
private static final ThreadLocal<CallContext> userContext = new ThreadLocal<>();
public static final CallContext getContext() {
CallContext context = userContext.get();
if (context == null) {
context = new CallContext();
userContext.set(context);
}
return userContext.get();
}
}
Then, I implemented a RestTemplate bean as follows:
#Bean
public RestTemplate getRestTemplate() {
RestTemplate template = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = template.getInterceptors();
interceptors.add(new CallContextInterceptor());
return template;
}
and the interceptor looks as follows:
public class CallContextInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add(CallContext.CORRELATION_ID, CallContextHolder.getContext().getCorrelationId());
return execution.execute(request, body);
}
}
When I call my endpoint, the servlet filter is executed and the correlation-id is stored in the CallContextHolder. So far, so good. However, the CallContextInterceptor seems to be called in an other thread and my ThreadLocal variable in the CallContextHolder is null. What I have to do to make this working?
#GetMapping("/ping")
public String ping() {
return pongRestTemplateClient.getPong();
}
Why not use Spring Cloud Sleuth and just let the libary do the work for you? http://cloud.spring.io/spring-cloud-sleuth/spring-cloud-sleuth.html
The problem was that I'm using Hysterix. Hystrix spawns a new thread to execute the code, completely unaware of the "outer" thread context. So, the executing thread losses access to the ThreadLocal dependant functionality when using Hysterix commands.
I found an answer to my problem here: https://github.com/jmnarloch/hystrix-context-spring-boot-starter

Spring Boot - Custom Filter/Stateless auth and #Secured annotation

I have been struggling with this for over 2 hours with no luck after reading around 10 different articles.
I want to use my custom filter to perform stateless authorization based on roles from DB and #Secured annotation.
Let's start with my example account identified in database by api-key: '6c1bb23e-e24c-41a5-8f12-72d3db0a6979'.
He has following String role fetched from DB: 'FREE_USER_ROLE'.
My filter:
public class ApiKeyAuthFilter extends OncePerRequestFilter {
private final AccountService accountService;
private final GlobalExceptionsAdvice exceptionAdvice;
private static final String API_KEY_HEADER_FIELD = "X-AUTH-KEY";
public static final List<String> NON_AUTH_END_POINTS
= Collections.unmodifiableList(Arrays.asList("/Accounts", "/Accounts/Login"));
AntPathMatcher pathMatcher = new AntPathMatcher();
public ApiKeyAuthFilter(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) {
this.accountService = accountService;
this.exceptionAdvice = exceptionAdvice;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain fc) throws ServletException, IOException {
Optional authKey = Optional.ofNullable(request.getHeader(API_KEY_HEADER_FIELD));
if (!authKey.isPresent()) {
sendForbiddenErrorMessage(response);
} else {
try {
AccountDTO account = accountService.findByApiKey(authKey.get().toString());
Set<GrantedAuthority> roles = new HashSet();
account.getRoles().forEach((singleRole) -> roles.add(new SimpleGrantedAuthority(singleRole.getName())));
Authentication accountAuth = new UsernamePasswordAuthenticationToken(account.getEmail(), account.getApiKey(),
roles);
SecurityContextHolder.getContext().setAuthentication(accountAuth);
SecurityContextHolder.getContext().getAuthentication().getAuthorities().forEach((role) -> {
System.out.println(role.getAuthority());
});
fc.doFilter(request, response);
} catch (ElementDoesNotExistException ex) {
//TODO: Add logging that user tried to falsy authenticate
sendForbiddenErrorMessage(response);
}
}
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return NON_AUTH_END_POINTS.stream().anyMatch(p -> {
return pathMatcher.match(p, request.getServletPath())
&& request.getMethod().equals("POST");
});
}
private void sendForbiddenErrorMessage(HttpServletResponse resp) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ErrorDetail error = exceptionAdvice.handleAccessDeniedException();
resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write(mapper.writeValueAsString(error));
}
As You can see I am using X-AUTH-KEY header to retrieve provided apiKey, then I fetch info from Database based on that key and assign appropiate roles into SecurityContextHolder. Until that point everything works. I am sending poper apiKey, DB returns 'FREE_USER_ROLE'.
My #Configuration annotation class. (I bet something is wrong here but I can not tell what):
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class ApiKeySecurityConfiguration extends WebSecurityConfigurerAdapter {
AccountService accountService;
GlobalExceptionsAdvice exceptionAdvice;
#Autowired
public ApiKeySecurityConfiguration(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) {
this.accountService = accountService;
this.exceptionAdvice = exceptionAdvice;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.csrf().disable();
httpSecurity.authorizeRequests().anyRequest().authenticated();
httpSecurity.addFilterBefore(new ApiKeyAuthFilter(accountService, exceptionAdvice), UsernamePasswordAuthenticationFilter.class);
}
}
And final piece of puzzle - Controller that uses #Secured:
#RestController
#RequestMapping("/Accounts")
public class AccountsResource {
#Secured({"FREE_USER_ROLE"})
#PutMapping()
public boolean testMethod() {
return true;
}
}
I have tried with both 'FREE_USER_ROLE' and 'ROLE_FREE_USER_ROLE'. Everytime I get 403 Forbidden.
So I have spent some more time yesterday on that and I have managed to get it working with #PreAuthorize annotation. Posting code below because it may be useful to someone in future.
Filter:
#Component
public class ApiKeyAuthFilter extends OncePerRequestFilter {
private final AccountService accountService;
private final GlobalExceptionsAdvice exceptionAdvice;
private static final String API_KEY_HEADER_FIELD = "X-AUTH-KEY";
public static final List<String> NON_AUTH_END_POINTS
= Collections.unmodifiableList(Arrays.asList("/Accounts", "/Accounts/Login"));
AntPathMatcher pathMatcher = new AntPathMatcher();
#Autowired
public ApiKeyAuthFilter(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) {
this.accountService = accountService;
this.exceptionAdvice = exceptionAdvice;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain fc) throws ServletException, IOException {
Optional authKey = Optional.ofNullable(request.getHeader(API_KEY_HEADER_FIELD));
if (!authKey.isPresent()) {
sendForbiddenErrorMessage(response);
} else {
try {
AccountDTO account = accountService.findByApiKey(authKey.get().toString());
Set<GrantedAuthority> roles = new HashSet();
account.getRoles().forEach((singleRole) -> roles.add(new SimpleGrantedAuthority(singleRole.getName())));
Authentication accountAuth = new UsernamePasswordAuthenticationToken(account.getEmail(), account.getApiKey(),
roles);
SecurityContextHolder.getContext().setAuthentication(accountAuth);
SecurityContextHolder.getContext().getAuthentication().getAuthorities().forEach((role) -> {
System.out.println(role.getAuthority());
});
fc.doFilter(request, response);
} catch (ElementDoesNotExistException ex) {
//TODO: Add logging that user tried to falsy authenticate
sendForbiddenErrorMessage(response);
}
}
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return NON_AUTH_END_POINTS.stream().anyMatch(p -> {
return pathMatcher.match(p, request.getServletPath())
&& request.getMethod().equals("POST");
});
}
private void sendForbiddenErrorMessage(HttpServletResponse resp) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ErrorDetail error = exceptionAdvice.handleAccessDeniedException();
resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write(mapper.writeValueAsString(error));
}
}
Configuration file:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApiKeySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Secured methods and methods allowed for anybody to use:
#RestController
#RequestMapping("/Accounts")
public class AccountsResource {
#PostMapping
#PreAuthorize("permitAll()")
public boolean forAll() {
return true;
}
#PutMapping()
#PreAuthorize("hasAuthority('FREE_USER_ROLE')")
public boolean testMethod() {
return true;
}
}

Resources