Java 如何使用 Spring MVC 创建我自己的过滤器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25373053/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:14:35  来源:igfitidea点击:

How to create my own filter with Spring MVC?

javaspringspring-mvc

提问by user2115378

I use Spring MVC (4.0.1) as a backend for rest services and angularjs as frontend.

我使用 Spring MVC (4.0.1) 作为休息服务的后端,使用 angularjs 作为前端。

every request to my server backend has a http-header with a session id

对我的服务器后端的每个请求都有一个带有会话 ID 的 http 标头

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!

现在我调用这个方法getPermission(xHeader)它只返回真或假。如果用户存在于我的数据库中,则返回 true 否则返回 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!

我现在想创建一个具有这种行为的过滤器,如果用户有权访问我的控制器,它会检查每个请求!但是如果该方法返回 false,它应该发回 401 错误并且不会到达我的控制器!

How can I do this and create my own filter? I use only Java Config and no XML.

我怎样才能做到这一点并创建我自己的过滤器?我只使用 Java Config 而没有使用 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};
    }
}

回答by Ben Potter

Spring canuse filters, but they recommend that you use their version of filters, known as an interceptor

Spring可以使用过滤器,但他们建议您使用他们的过滤器版本,称为拦截器

http://viralpatel.net/blogs/spring-mvc-interceptor-example/

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.

快速浏览它们的工作方式。它们几乎与过滤器相同,但旨在在 Spring MVC 生命周期内工作。

回答by Avinash

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()};
    }
}

回答by Lazar Lazarov

I assume that you are trying to implement some kind of OAuth security which is based on jwt token.

我假设您正在尝试实现某种基于 jwt 令牌的 OAuth 安全性。

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:

当然,我们有 Main ,您可以在其中看到过滤器 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);
    }
}

Hereis a link to GitHub all thanks goes to nielsutrechtfor the great work I have used this project as base and it works perfectly.

是一个指向 GitHub 的链接,感谢nielsutrecht 所做的出色工作,我以这个项目为基础,它运行良好。

回答by ares

Alternative to Filters, you can use HandlerInterceptor.

除了过滤器,您还可以使用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.

然后将此拦截器添加到您的 webmvc 配置中。

@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
    }

}

回答by Web Clerisy

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.

1)通过实现过滤器接口并覆盖其方法来创建您的类。

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

2) 现在在 web.xml 中配置您的过滤器

<filter>
  <filter-name>myFilter</filter-name>
  <filter-class>MyFilter</filter-class>
</filter>

3) Now provide url mapping of the filter.

3) 现在提供过滤器的 url 映射。

<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.

4) 现在重新启动您的服务器并检查所有 Web 请求将首先来到 MyFilter,然后继续到相应的控制器。

Hopefully it will be the required answer.

希望这将是必需的答案。

回答by nille85

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.

您还可以使用具有针对某个注释的切入点的方面来实现它。我编写了一个库,使您能够使用基于 JWT 令牌执行授权检查的注释。

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.

您可以在以下位置找到包含所有文档的项目:https: //github.com/nille85/jwt-aspect。我多次使用这种方法来保护由单页应用程序使用的 REST 后端。

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/

我还在我的博客中记录了如何在 Spring MVC 应用程序中使用它: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

以下摘自https://github.com/nille85/auth-server上的示例项目

The example underneath contains a protected method getClient. The annotation @Authorizethat 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.

下面的示例包含一个受保护的方法 getClient。方面使用的注解@Authorize会检查“aud jwt claim”中的值是否与使用@ClaimValue 进行注解的 clientId 参数匹配 。如果匹配,则可以输入该方法。否则抛出异常。

@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:

Aspect 本身可以配置为:

@Bean
public JWTAspect jwtAspect() {
    JWTAspect aspect = new JWTAspect(payloadService());
    return aspect;
}

The PayloadService that is needed can for example be implemented like:

例如,所需的 PayloadService 可以像这样实现:

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;
    }

}

回答by sura2k

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 FilterRegistrationBeanif you Spring Boot. It does the same thing (I think so) which FilterRegistration.Dynamicdoes.

此外,FilterRegistrationBean如果您使用Spring Boot,则可以使用。它做同样的事情(我认为是这样)FilterRegistration.Dynamic