如何使用Spring MVC创建自己的filter?

我使用Spring MVC(4.0.1)作为rest服务的后端和angularjs作为前端。

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

我可以使用以下代码在服务器后端读取此标头:

@Autowired protected HttpServletRequest request; String xHeader=request.getHeader("X-Auth-Token"); //returns the sessionID from the header 

现在我调用此方法getPermission(xHeader)它只返回true或false。 如果用户存在于我的数据库中,则返回true,否则为false!

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

我该怎么做并创建自己的filter? 我只使用Java Config而不使用XML。

我想我必须在这里添加filter:

 public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Filter[] getServletFilters() { MyOwnFilter=new MyOwnFilter(); return new Filter[] {MyOwnFilter}; } } 

替代Filter,您可以使用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 { } } 

然后将此拦截器添加到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 } } 

下面是执行您提到的逻辑的filter

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

你做对了,弹簧配置应该跟随。

 public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Filter[] getServletFilters() { return new Filter[]{new AuthTokenFilter()}; } } 

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

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

快速了解它们的工作方式。 它们几乎与filter完全相同,但设计用于在Spring MVC生命周期内工作。

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

现在有几种方法可以这样做,但这是我最喜欢的方法:

以下是filter的外观:

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

非常简单,还有用户控制器,您可以在其中找到登录方法:

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

当然我们有Main你可以在哪里看到filterbean:

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

最后但并非最不重要的是有一个示例控制器:

 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) claims.get("roles")).contains(role); } } 

这里有一个指向GitHub的链接,所有感谢nielsutrecht对于我用这个项目作为基础的伟大工作,它完美地工作。

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

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

我还在博客中记录了如何在Spring MVC应用程序中使用它: http : //www.nille.be/security/creating-authorization-server-using-jwts/

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

下面的示例包含受保护的方法getClient。 方面使用的注释@Authorize检查来自“aud jwt claim”的值是否与使用@ClaimValue注释的clientId参数匹配。 如果匹配,则可以输入该方法。 否则抛出exception。

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

Aspect本身可以配置为:

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

所需的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; } } 

您可以通过执行以下步骤来创建和配置自己的filter。

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

 public class MyFilter implements javax.servlet.Filter{ public void destroy(){} public void doFilter(Request, Response, FilterChain){//do what you want to filter } ........ } 

2)现在在web.xml中配置filter

  myFilter MyFilter  

3)现在提供filter的url映射。

  myFilter *  

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

希望这将是必要的答案。

你的方法看起来正确。

一旦我使用了类似于以下的东西(删除了大部分行并保持简单)。

 public class MvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); EnumSet 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[] { "/" }; } } 

您还需要一个自定义filter,如下所示。

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

希望这可以帮助。

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