In a webapp I'm building using Spring Boot & MVC and am trying to deny access to for all URL's except /signin for users that are not logged in. To achieve this I've setup an implementation of HandlerInterceptor where the preHandler should route all non-valid requests to the /signin page.
The setup:
LoginViewController
package com.controller;
import com.model.UserDao;
import com.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
#Controller
#RequestMapping(value = "/signin")
#SessionAttributes("username")
public class LoginViewController {
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showLoginForm(){
return new ModelAndView("login");
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView verifyLogin(HttpServletRequest request, HttpSession session) {
ModelAndView modelAndView;
String username = request.getParameter("username");
// if login fails, set reload login page
if (userService.verifyUserLogin(username,request.getParameter("password")) == null){
modelAndView = new ModelAndView("login");
modelAndView.addObject("login_failed", true);
} else {
modelAndView = new ModelAndView("index");
session.setAttribute("username", username);
}
return modelAndView;
}
}
AccessInterceptor
package com.spring.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
public class AccessInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println(request.getRequestURI());
try {
if (!request.getRequestURI().endsWith("/signin")) {
if (request.getSession()
.getAttribute("username") == null) {
response.sendRedirect(request.getContextPath() + "/signin");
return false;
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
return true;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Post-handle");
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("After completion handle");
}
}
WebApplicationConfig
package com.spring;
import com.spring.interceptor.AccessInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
#Configuration
#EnableWebMvc
public class WebApplicationConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(
new AccessInterceptor()).
addPathPatterns("/**").
excludePathPatterns("/signin/**").
excludePathPatterns("/static/**");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
WebApplicationInitializer
package com.spring;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(WebApplicationConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(MyWebAppInitializer.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcherServlet", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
dispatcher.addMapping("*.css");
dispatcher.addMapping("*.eot");
dispatcher.addMapping("*.svg");
dispatcher.addMapping("*.ttf");
dispatcher.addMapping("*.woff");
dispatcher.addMapping("*.map");
dispatcher.addMapping("*.js");
dispatcher.addMapping("*.ico");
}
}
Now the problem is that the System.out.println(request.getRequestURI()) in AccessInterceptor always prints /error. So the request is always redirected even when calling /signin. Another interesting thing is that no CSS or other static resources are rendered, even with the dispacher mapping configured.
Any Ideas?
if you missing your payload, the api url couldn't recognize. at least put something
I also met this problem,in WebLogIntercept(your MyWebAppInitializer) class
I solved this problem using the following code
add this two func
private Class getClassByName(Class classObject, String name){
Map<Class,List<Field>> fieldMap = new HashMap<>();
Class returnClass = null;
Class tempClass = classObject;
while (tempClass != null) {
fieldMap.put(tempClass,Arrays.asList(tempClass .getDeclaredFields()));
tempClass = tempClass.getSuperclass();
}
for(Map.Entry<Class,List<Field>> entry: fieldMap.entrySet()){
for (Field f : entry.getValue()) {
if(f.getName().equals(name)){
returnClass = entry.getKey();
break;
}
}
}
return returnClass;
}
private Object findCoyoteRequest(Object request) throws Exception {
Class a = getClassByName(request.getClass(),"request");
Field request1 = a.getDeclaredField("request");
request1.setAccessible(true);
Object b = request1.get(request);
if(getClassByName(b.getClass(),"coyoteRequest") == null){
return findCoyoteRequest(b);
}else{
return b;
}
and use this code
Object a = findCoyoteRequest(request);
Field coyoteRequest = a.getClass().getDeclaredField("coyoteRequest");
coyoteRequest.setAccessible(true);
Object b = coyoteRequest.get(a);
Field uriMB = b.getClass().getDeclaredField("uriMB");
uriMB.setAccessible(true);
MessageBytes c = (MessageBytes)uriMB.get(b);
System.out.println(c.getString());
c.getString() is realuri
my english is not good,Hope useful
Disabling CFR worked for me
See https://www.baeldung.com/spring-security-csrf
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
I have the same function to implement as you. And I finally found that there was nothing wrong with this function. The "/error" request actually exists, maybe sent by the servlet container or whatever(i don't know). It exists to show you the exception on the browser.
When I put "/error" to my white-list(I also put paths like "/login" which don't need the authority), the e.printStackTrace(); information just show on the browser.
sometimes,you can try rebuild project,here is my code:
if(request.getRequestURI().startsWith("/user/")) {return true;}
it always return "/error"
Related
I use Spring MVC (4.0.1) as a backend for rest services and angularjs as frontend.
every request to my server backend has a http-header with a session id
I can read this header in my server backend with the following code:
#Autowired
protected HttpServletRequest request;
String xHeader=request.getHeader("X-Auth-Token"); //returns the sessionID from the header
Now I call this method getPermission(xHeader) it return only true or false. If the user exists in my DB it return true else false!
I want now create a filter with this behavior, that checks every request if the user have the permission to access my controllers! But if the method returns false it should send back a 401 error and not reach my controller!
How can I do this and create my own filter? I use only Java Config and no XML.
I think I must add the filter here:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
MyOwnFilter=new MyOwnFilter();
return new Filter[] {MyOwnFilter};
}
}
Alternative to Filters, you can use HandlerInterceptor.
public class SessionManager implements HandlerInterceptor{
// This method is called before the controller
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String xHeader = request.getHeader("X-Auth-Token");
boolean permission = getPermission(xHeader);
if(permission) {
return true;
}
else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
// Above code will send a 401 with no response body.
// If you need a 401 view, do a redirect instead of
// returning false.
// response.sendRedirect("/401"); // assuming you have a handler mapping for 401
}
return false;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
And then add this interceptor to your webmvc config.
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
SessionManager getSessionManager() {
return new SessionManager();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionManager())
.addPathPatterns("/**")
.excludePathPatterns("/resources/**", "/login");
// assuming you put your serve your static files with /resources/ mapping
// and the pre login page is served with /login mapping
}
}
Below is the filter to perform the logic you have mentioned
#WebFilter("/*")
public class AuthTokenFilter implements Filter {
#Override
public void destroy() {
// ...
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String xHeader = ((HttpServletRequest)request).getHeader("X-Auth-Token");
if(getPermission(xHeader)) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("401.html").forward(request, response);
}
}
}
And you got it right, the spring config should be following.
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new AuthTokenFilter()};
}
}
Spring can use filters, but they recommend that you use their version of filters, known as an interceptor
http://viralpatel.net/blogs/spring-mvc-interceptor-example/
There is a quick run through of how they work. They are nearly identical to filters, but designed to work inside the Spring MVC lifecycle.
I assume that you are trying to implement some kind of OAuth security which is based on jwt token.
Nowdays there are several ways to do so but here is my favourite one:
Here is how the filter looks like:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class JwtFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest req,
final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
final String token = authHeader.substring(7); // The part after "Bearer "
try {
final Claims claims = Jwts.parser().setSigningKey("secretkey")
.parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
}
catch (final SignatureException e) {
throw new ServletException("Invalid token.");
}
chain.doFilter(req, res);
}
}
Pretty simple there is the user controller also where you can find the login method:
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#RestController
#RequestMapping("/user")
public class UserController {
private final Map<String, List<String>> userDb = new HashMap<>();
public UserController() {
userDb.put("tom", Arrays.asList("user"));
userDb.put("sally", Arrays.asList("user", "admin"));
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResponse login(#RequestBody final UserLogin login)
throws ServletException {
if (login.name == null || !userDb.containsKey(login.name)) {
throw new ServletException("Invalid login");
}
return new LoginResponse(Jwts.builder().setSubject(login.name)
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact());
}
#SuppressWarnings("unused")
private static class UserLogin {
public String name;
public String password;
}
#SuppressWarnings("unused")
private static class LoginResponse {
public String token;
public LoginResponse(final String token) {
this.token = token;
}
}
}
Of course we have Main where you can see the filter bean:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#EnableAutoConfiguration
#ComponentScan
#Configuration
public class WebApplication {
#Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
public static void main(final String[] args) throws Exception {
SpringApplication.run(WebApplication.class, args);
}
}
Last but not least there is an example controller:
import io.jsonwebtoken.Claims;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api")
public class ApiController {
#SuppressWarnings("unchecked")
#RequestMapping(value = "role/{role}", method = RequestMethod.GET)
public Boolean login(#PathVariable final String role,
final HttpServletRequest request) throws ServletException {
final Claims claims = (Claims) request.getAttribute("claims");
return ((List<String>) claims.get("roles")).contains(role);
}
}
Here is a link to GitHub all thanks goes to nielsutrecht for the great work I have used this project as base and it works perfectly.
You can also implement it using an aspect with a pointcut that targets a certain annotation. I have written a library that enables you to use annotations that perform authorization checks based on a JWT token.
You can find the project with all the documentation on: https://github.com/nille85/jwt-aspect. I have used this approach multiple times in order to secure a REST Backend that is consumed by a single page application.
I have also documented on my blog how you can use it in a Spring MVC Application: http://www.nille.be/security/creating-authorization-server-using-jwts/
The following is an extract from the example project on https://github.com/nille85/auth-server
The example underneath contains a protected method getClient. The annotation #Authorize that the aspect uses checks if the value from the "aud jwt claim" matches the clientId parameter that is annotated with #ClaimValue. If it matches, the method can be entered. Otherwise an exception is thrown.
#RestController
#RequestMapping(path = "/clients")
public class ClientController {
private final ClientService clientService;
#Autowired
public ClientController(final ClientService clientService) {
this.clientService = clientService;
}
#Authorize("hasClaim('aud','#clientid')")
#RequestMapping(value = "/{clientid}", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client getClient(#PathVariable(value = "clientid") #ClaimValue(value = "clientid") final String clientId) {
return clientService.getClient(clientId);
}
#RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody List<Client> getClients() {
return clientService.getClients();
}
#RequestMapping(path = "", method = RequestMethod.POST, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client registerClient(#RequestBody RegisterClientCommand command) {
return clientService.register(command);
}
}
The Aspect itself can be configured like:
#Bean
public JWTAspect jwtAspect() {
JWTAspect aspect = new JWTAspect(payloadService());
return aspect;
}
The PayloadService that is needed can for example be implemented like:
public class PayloadRequestService implements PayloadService {
private final JWTVerifier verifier;
public PayloadRequestService(final JWTVerifier verifier){
this.verifier = verifier;
}
#Override
public Payload verify() {
ServletRequestAttributes t = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = t.getRequest();
final String jwtValue = request.getHeader("X-AUTH");
JWT jwt = new JWT(jwtValue);
Payload payload =verifier.verify(jwt);
return payload;
}
}
You can create and configure your own filter by doing following steps.
1) Create your class by implementing the filter interface and override its methods.
public class MyFilter implements javax.servlet.Filter{
public void destroy(){}
public void doFilter(Request, Response, FilterChain){//do what you want to filter
}
........
}
2) Now configure your filter in web.xml
<filter>
<filter-name>myFilter</filter-name>
<filter-class>MyFilter</filter-class>
</filter>
3) Now provide url mapping of the filter.
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
4) Now restart your server and check all the web request will first come to MyFilter and then proceed to the respective controller.
Hopefully it will be the required answer.
Your approach looks correct.
Once I have used something similar to following (Removed most of the lines and kept it simple).
public class MvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ERROR);
FilterRegistration.Dynamic monitoringFilter = servletContext.addFilter("monitoringFilter", MonitoringFilter.class);
monitoringFilter.addMappingForUrlPatterns(dispatcherTypes, false, "/api/admin/*");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Also you need a custom filter looks like below.
public class CustomXHeaderFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String xHeader = request.getHeader("X-Auth-Token");
if(YOUR xHeader validation fails){
//Redirect to a view
//OR something similar
return;
}else{
//If the xHeader is OK, go through the chain as a proper request
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Hope this helps.
Additionally you can use FilterRegistrationBean if you Spring Boot. It does the same thing (I think so) which FilterRegistration.Dynamic does.
I am currently trying to implement a customized error handler for spring boot and I have done it with the following:
public class ExceptionHandler extends HandlerInterceptorAdapter {
public static Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, #Nullable ModelAndView modelAndView) throws Exception {
try {
log.info("Service {} Calling {} on {} finished with status {}",request.getRemoteUser(), request.getMethod(), request.getRequestURI(), HttpStatus.valueOf(response.getStatus()));
} catch (Exception e) {
// Do nothing
} finally {
log.error("[Spring Boot Interceptor] {} returned with {}", handler, HttpStatus.valueOf(response.getStatus()));
}
}
Somehow this does not work, and the exception is still thrown to the client, is there some way to catch the exception thrown by the method and ignore it for example.
A good way to manage the exception is using #ControllerAdvice, using this you may handle any kind of exception and customize the response as required.
As said in the comment, you have to add InterceptorRegistry to register the interceptor.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor()).addPathPatterns("/**");
}
}
The catch block inside postHandle will only be executed if an exception occurred inside the try-catch block as below,
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, #Nullable ModelAndView modelAndView) throws Exception {
try {
int error = 1/0;
} catch (Exception e) {
log.info("Exception will be handled inside catch block");
}
}
Now let's explore the #ControllerAdvice to manage the exception within the application. These two APIs will generate the exceptions and we will manage the exceptions using #ExceptionHandler
#GetMapping("/exception/404")
public void generateResourceNotFound() {
throw new ResourceNotFoundException("resource not found");
}
#GetMapping("/exception/403")
public void generateAccessDenied() {
throw new AccessDeniedException("access denied");
}
GlobalExceptionHandler.java
import com.learning.annotations.controller.ResourceNotFoundException;
import com.learning.annotations.dto.ErrorResponseDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public Logger log = LoggerFactory.getLogger(Interceptor.class);
#ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponseDTO> handleAccessDeniedException(AccessDeniedException ex, WebRequest request) {
ErrorResponseDTO response = new ErrorResponseDTO();
response.setError(ex.getMessage());
response.setMessage("You don't have authority to access the resource");
return new ResponseEntity<>(response, HttpStatus.FORBIDDEN);
}
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponseDTO> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ErrorResponseDTO response = new ErrorResponseDTO();
response.setError(ex.getMessage());
response.setMessage("Resource might be moved temporary or not available");
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
}
To customize the response we can create error response DTO as follows,
import lombok.Data;
#Data
public class ErrorResponseDTO {
private String message;
private String error;
}
I have been struggling to get waffle to work with spring 4.2.5 using spring java configuration. And I thought I might as well help others in the same situation.
We use a custom preWaffle and postWaffle filter to authenticate that the user exists in our database after it has been validated via waffles NTLM protocol.
We also have methods for authorization of a user actions using the EnableGlobalMethodSecurity annotation.
To get this working in spring java configuration was trouble some to say the least. You can find our solution in the answer below. I hope it will help.
SpringConfiguration.java
// ... imports
#Configuration
#EnableWebMvc
#EnableScheduling
#PropertySources({
#PropertySource("classpath:app.properties")
// ... Properties sources
})
#EnableTransactionManagement
#ComponentScan(basePackages = "com.our.package")
public class SpringConfiguration extends WebMvcConfigurerAdapter {
// Our Spring configuration ...
}
SecurityConfiguration.java
// ... imports
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
#Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
// Authentication manager configuration
#Autowired
private WindowsAuthenticationProviderWrapper authProvider;
#Autowired
private AuthenticationManagerBuilder auth;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Bean
public AuthenticationManager authenticationManager() throws Exception {
return auth.getObject();
}
// Waffle configuration
#Bean
public Filter customPreAuthSecurityFilter() {
return new CustomPreAuthSecurityFilter();
}
#Bean
public Filter customNegotiateSecurityFilter() {
return new CustomNegotiateSecurityFilter();
}
#Bean
public WindowsAuthProviderImpl waffleAuthProvider(){
return new WindowsAuthProviderImpl();
}
#Bean(name="negotiateSecurityFilterProvider")
#Autowired
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(){
NegotiateSecurityFilterProvider bean = new NegotiateSecurityFilterProvider(waffleAuthProvider());
List<String> protocols = new ArrayList<>();
protocols.add("Negotiate");
bean.setProtocols(protocols);
return bean;
}
#Bean
public BasicSecurityFilterProvider basicSecurityFilterProvider(){
return new BasicSecurityFilterProvider(waffleAuthProvider());
}
#Bean(name="waffleSecurityFilterProviderCollection")
#Autowired
public waffle.servlet.spi.SecurityFilterProviderCollection negotiateSecurityFilterProviderCollection() {
final List<SecurityFilterProvider> lsp = new ArrayList<>();
lsp.add(negotiateSecurityFilterProvider());
lsp.add(basicSecurityFilterProvider());
return new waffle.servlet.spi.SecurityFilterProviderCollection(lsp.toArray(new SecurityFilterProvider[]{}));
}
#Bean(name="negotiateSecurityFilterEntryPoint")
#Autowired
public waffle.spring.NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint() {
final waffle.spring.NegotiateSecurityFilterEntryPoint ep = new waffle.spring.NegotiateSecurityFilterEntryPoint();
ep.setProvider(negotiateSecurityFilterProviderCollection());
return ep;
}
#Bean(name="negotiateSecurityFilter")
#Autowired
public waffle.spring.NegotiateSecurityFilter waffleNegotiateSecurityFilter(){
waffle.spring.NegotiateSecurityFilter bean = new waffle.spring.NegotiateSecurityFilter();
bean.setRoleFormat("both");
bean.setPrincipalFormat("fqn");
bean.setAllowGuestLogin(false);
bean.setProvider(negotiateSecurityFilterProviderCollection());
return bean;
}
// Static Mappings
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
// Security filter chain
// The custom filters can be removed if you only use waffle
// but this is how we added them
#Override
protected void configure(HttpSecurity http) throws Exception {
// A user needs to have the role user and has to be authenticated
http.exceptionHandling()
.authenticationEntryPoint(negotiateSecurityFilterEntryPoint()).and()
.addFilterBefore(customPreAuthSecurityFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(waffleNegotiateSecurityFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(customNegotiateSecurityFilter(), BasicAuthenticationFilter.class)
.authorizeRequests().anyRequest().fullyAuthenticated();
}
}
To be able to autowire the waffle authProvider I created the following wrapperclass.
WindowsAuthenticationProviderWrapper.java
// ... imports
// This class purpose is only to make the Windows authentication provider autowireable in spring.
#Component
public class WindowsAuthenticationProviderWrapper extends WindowsAuthenticationProvider{}
As requested (Some code has been stripped due to security risks).
CustomPreAuthFilter.java
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* This filter removes the excess negoatiate header sent by IE. If the client
* has already authenticated, strip the Authorization header from the request.
*/
public class CustomPreAuthSecurityFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
SecurityContext sec = SecurityContextHolder.getContext();
HttpServletRequest req = (HttpServletRequest) servletRequest;
if(sec != null && sec.getAuthentication() != null) {
req = new CustomServletRequestWrapper(req);
}
try {
filterChain.doFilter(req, servletResponse);
} catch (RuntimeException e) {
sendUnauthorized((HttpServletResponse) servletResponse);
}
}
private void sendUnauthorized(HttpServletResponse response) throws IOException {
logger.warn("error logging in user");
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
CustomNegotiateSecurityFilter.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import waffle.servlet.WindowsPrincipal;
import waffle.spring.WindowsAuthenticationToken;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* Handle post NTLM authentication against system database
*/
public class CustomNegotiateSecurityFilter extends GenericFilterBean {
#Autowired
private UserDAO userDAO;
#Autowired
Environment env;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomNegotiateSecurityFilter.class);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
SecurityContext sec = SecurityContextHolder.getContext();
Authentication authentication = sec.getAuthentication();
// Continue filter chain if we are anonymously authenticated or if DB authentication has already happened.
if (authentication != null && authentication.getClass() == WindowsAuthenticationToken.class) {
// The user is Authenticated with NTLM but needs to be checked against the DB.
User user;
try {
// fetch user from DB ...
} catch (Exception e) {
// The could not be found in the DB.
sendUnauthorized(response);
return;
}
// The user was found in the DB.
WindowsPrincipal principal = (WindowsPrincipal)authentication.getPrincipal();
final CustomAuthenticationToken token = new CustomAuthenticationToken(principal); // This class extends WindowsAuthenticationToken
// add roles to token ...
sec.setAuthentication(token);
}
chain.doFilter(request, response);
}
private void sendUnauthorized(HttpServletResponse response) throws IOException {
logger.warn("Could not log in user");
SecurityContextHolder.clearContext();
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
private void addRoleToAuthentication(WindowsAuthenticationToken authentication, String role) {
for(GrantedAuthority authority : authentication.getAuthorities()) {
if(authority.getAuthority().equals(role)) {
return;
}
}
authentication.getAuthorities().add(new SimpleGrantedAuthority(role));
}
}
EDIT
For those who asked about here is one implementation. CustomServletRequestWrapper:
class CustomServletRequestWrapper extends HttpServletRequestWrapper {
public CustomServletRequestWrapper(HttpServletRequest request) {
super(request);
}
public String getHeader(String name) {
if(name.equals("Authorization"))
return null;
String header = super.getHeader(name);
return (header != null) ? header : super.getParameter(name); // Note: you can't use getParameterValues() here.
}
public Enumeration getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
names.addAll(Collections.list(super.getParameterNames()));
names.remove("Authorization");
return Collections.enumeration(names);
}
}
If you need more information don't hessitate to ask.
I am trying to setup authentication using JWT using Angular as my front end and Spring as backend. I have the following AuthenticationFilter class for setting up JWT filter:
package com.atlas.config;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class AuthenticationFilter implements Filter {
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//System.out.println("filter");
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String authHeader = ((HttpServletRequest) request).getHeader("Authorizatoin");
//System.out.println(authHeader);
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
String token = authHeader.substring(7);
try
{
//security key needs to be changed before deployment
Claims claims = Jwts.parser().setSigningKey("feb1atlaskey").parseClaimsJws(token).getBody();
req.setAttribute("claims", claims);
}
catch(SignatureException e)
{
throw new ServletException("Not a valid token");
}
chain.doFilter(req,res);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
This is my UserController class where the login check and setting Auth header happens:
package com.atlas.controller;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.atlas.entity.User;
import com.atlas.service.UserService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#RestController
#RequestMapping("/users")
public class UserController {
private final Map<String, List<String>> userDb = new HashMap<>();
public UserController() {
userDb.put("tom", Arrays.asList("user"));
userDb.put("sally", Arrays.asList("user", "admin"));
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResponse login(#RequestBody final UserLogin login)
throws ServletException {
System.out.println("In user controller");
if (login.name == null || !userDb.containsKey(login.name)) {
throw new ServletException("Invalid login");
}
return new LoginResponse(Jwts.builder().setSubject(login.name)
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact());
}
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public void addUser(#RequestBody User u) {
this.userService.addUser(u);
}
private static class UserLogin {
public String name;
public String password;
}
private static class LoginResponse {
public String token;
public LoginResponse(final String token) {
this.token = token;
}
}
}
Now, when I do a login using simple form and make a POST request using Angular, its not reaching UserController. Its hitting the filter and throwing Servlet Exception as "Missing or invalid Authorization header".
I have also configured my app to add this filter in the chain as below:
package com.atlas.config;
import javax.servlet.Filter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInit extends AbstractAnnotationConfigDispatcherServletInitializer{
#Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class[]{AppConfig.class,SwaggerConfig.class,AuthenticationFilter.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
#Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] {"/api/*"};
}
#Override
protected Filter[] getServletFilters() {
Filter [] singleton = { new CORSFilter(), new AuthenticationFilter() };
return singleton;
}
}
Am I missing some config here or making some terrible mistake.
Any help much appreciated!
The "Authorization" header is mispelled:
Replace from:
String authHeader = ((HttpServletRequest) request).getHeader("Authorizatoin");
To:
String authHeader = ((HttpServletRequest) request).getHeader("Authorization");
You may consider using Spring Framework constants for http headers to avoid such issues:
String authHeader = ((HttpServletRequest) request).getHeader(org.springframework.http.HttpHeaders.AUTHORIZATION);
I use Spring MVC (4.0.1) as a backend for rest services and angularjs as frontend.
every request to my server backend has a http-header with a session id
I can read this header in my server backend with the following code:
#Autowired
protected HttpServletRequest request;
String xHeader=request.getHeader("X-Auth-Token"); //returns the sessionID from the header
Now I call this method getPermission(xHeader) it return only true or false. If the user exists in my DB it return true else false!
I want now create a filter with this behavior, that checks every request if the user have the permission to access my controllers! But if the method returns false it should send back a 401 error and not reach my controller!
How can I do this and create my own filter? I use only Java Config and no XML.
I think I must add the filter here:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
MyOwnFilter=new MyOwnFilter();
return new Filter[] {MyOwnFilter};
}
}
Alternative to Filters, you can use HandlerInterceptor.
public class SessionManager implements HandlerInterceptor{
// This method is called before the controller
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String xHeader = request.getHeader("X-Auth-Token");
boolean permission = getPermission(xHeader);
if(permission) {
return true;
}
else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
// Above code will send a 401 with no response body.
// If you need a 401 view, do a redirect instead of
// returning false.
// response.sendRedirect("/401"); // assuming you have a handler mapping for 401
}
return false;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
And then add this interceptor to your webmvc config.
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
SessionManager getSessionManager() {
return new SessionManager();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getSessionManager())
.addPathPatterns("/**")
.excludePathPatterns("/resources/**", "/login");
// assuming you put your serve your static files with /resources/ mapping
// and the pre login page is served with /login mapping
}
}
Below is the filter to perform the logic you have mentioned
#WebFilter("/*")
public class AuthTokenFilter implements Filter {
#Override
public void destroy() {
// ...
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String xHeader = ((HttpServletRequest)request).getHeader("X-Auth-Token");
if(getPermission(xHeader)) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("401.html").forward(request, response);
}
}
}
And you got it right, the spring config should be following.
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new AuthTokenFilter()};
}
}
Spring can use filters, but they recommend that you use their version of filters, known as an interceptor
http://viralpatel.net/blogs/spring-mvc-interceptor-example/
There is a quick run through of how they work. They are nearly identical to filters, but designed to work inside the Spring MVC lifecycle.
I assume that you are trying to implement some kind of OAuth security which is based on jwt token.
Nowdays there are several ways to do so but here is my favourite one:
Here is how the filter looks like:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class JwtFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest req,
final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header.");
}
final String token = authHeader.substring(7); // The part after "Bearer "
try {
final Claims claims = Jwts.parser().setSigningKey("secretkey")
.parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
}
catch (final SignatureException e) {
throw new ServletException("Invalid token.");
}
chain.doFilter(req, res);
}
}
Pretty simple there is the user controller also where you can find the login method:
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#RestController
#RequestMapping("/user")
public class UserController {
private final Map<String, List<String>> userDb = new HashMap<>();
public UserController() {
userDb.put("tom", Arrays.asList("user"));
userDb.put("sally", Arrays.asList("user", "admin"));
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public LoginResponse login(#RequestBody final UserLogin login)
throws ServletException {
if (login.name == null || !userDb.containsKey(login.name)) {
throw new ServletException("Invalid login");
}
return new LoginResponse(Jwts.builder().setSubject(login.name)
.claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact());
}
#SuppressWarnings("unused")
private static class UserLogin {
public String name;
public String password;
}
#SuppressWarnings("unused")
private static class LoginResponse {
public String token;
public LoginResponse(final String token) {
this.token = token;
}
}
}
Of course we have Main where you can see the filter bean:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#EnableAutoConfiguration
#ComponentScan
#Configuration
public class WebApplication {
#Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
public static void main(final String[] args) throws Exception {
SpringApplication.run(WebApplication.class, args);
}
}
Last but not least there is an example controller:
import io.jsonwebtoken.Claims;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api")
public class ApiController {
#SuppressWarnings("unchecked")
#RequestMapping(value = "role/{role}", method = RequestMethod.GET)
public Boolean login(#PathVariable final String role,
final HttpServletRequest request) throws ServletException {
final Claims claims = (Claims) request.getAttribute("claims");
return ((List<String>) claims.get("roles")).contains(role);
}
}
Here is a link to GitHub all thanks goes to nielsutrecht for the great work I have used this project as base and it works perfectly.
You can also implement it using an aspect with a pointcut that targets a certain annotation. I have written a library that enables you to use annotations that perform authorization checks based on a JWT token.
You can find the project with all the documentation on: https://github.com/nille85/jwt-aspect. I have used this approach multiple times in order to secure a REST Backend that is consumed by a single page application.
I have also documented on my blog how you can use it in a Spring MVC Application: http://www.nille.be/security/creating-authorization-server-using-jwts/
The following is an extract from the example project on https://github.com/nille85/auth-server
The example underneath contains a protected method getClient. The annotation #Authorize that the aspect uses checks if the value from the "aud jwt claim" matches the clientId parameter that is annotated with #ClaimValue. If it matches, the method can be entered. Otherwise an exception is thrown.
#RestController
#RequestMapping(path = "/clients")
public class ClientController {
private final ClientService clientService;
#Autowired
public ClientController(final ClientService clientService) {
this.clientService = clientService;
}
#Authorize("hasClaim('aud','#clientid')")
#RequestMapping(value = "/{clientid}", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client getClient(#PathVariable(value = "clientid") #ClaimValue(value = "clientid") final String clientId) {
return clientService.getClient(clientId);
}
#RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody List<Client> getClients() {
return clientService.getClients();
}
#RequestMapping(path = "", method = RequestMethod.POST, produces = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Client registerClient(#RequestBody RegisterClientCommand command) {
return clientService.register(command);
}
}
The Aspect itself can be configured like:
#Bean
public JWTAspect jwtAspect() {
JWTAspect aspect = new JWTAspect(payloadService());
return aspect;
}
The PayloadService that is needed can for example be implemented like:
public class PayloadRequestService implements PayloadService {
private final JWTVerifier verifier;
public PayloadRequestService(final JWTVerifier verifier){
this.verifier = verifier;
}
#Override
public Payload verify() {
ServletRequestAttributes t = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = t.getRequest();
final String jwtValue = request.getHeader("X-AUTH");
JWT jwt = new JWT(jwtValue);
Payload payload =verifier.verify(jwt);
return payload;
}
}
You can create and configure your own filter by doing following steps.
1) Create your class by implementing the filter interface and override its methods.
public class MyFilter implements javax.servlet.Filter{
public void destroy(){}
public void doFilter(Request, Response, FilterChain){//do what you want to filter
}
........
}
2) Now configure your filter in web.xml
<filter>
<filter-name>myFilter</filter-name>
<filter-class>MyFilter</filter-class>
</filter>
3) Now provide url mapping of the filter.
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
4) Now restart your server and check all the web request will first come to MyFilter and then proceed to the respective controller.
Hopefully it will be the required answer.
Your approach looks correct.
Once I have used something similar to following (Removed most of the lines and kept it simple).
public class MvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ERROR);
FilterRegistration.Dynamic monitoringFilter = servletContext.addFilter("monitoringFilter", MonitoringFilter.class);
monitoringFilter.addMappingForUrlPatterns(dispatcherTypes, false, "/api/admin/*");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebMvcConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Also you need a custom filter looks like below.
public class CustomXHeaderFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String xHeader = request.getHeader("X-Auth-Token");
if(YOUR xHeader validation fails){
//Redirect to a view
//OR something similar
return;
}else{
//If the xHeader is OK, go through the chain as a proper request
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Hope this helps.
Additionally you can use FilterRegistrationBean if you Spring Boot. It does the same thing (I think so) which FilterRegistration.Dynamic does.