本节描述 Spring Security 如何与 Servlet API 集成。servletapi-xml 示例应用程序演示了这些方法中的每种方法的用法。
HttpServletRequest.getRemoteUser() 将返回
HttpServletRequest.getUserPrincipal() 将返回 Authentication auth = httpServletRequest.getUserPrincipal(); // assume integrated custom UserDetails called MyCustomUserDetails // by default, typically instance of UserDetails MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal(); String firstName = userDetails.getFirstName(); String lastName = userDetails.getLastName();
HttpServletRequest.isUserInRole(String) 将确定 boolean isAdmin = httpServletRequest.isUserInRole("ADMIN"); 这可能有助于确定是否应该显示某些 UI 组件。例如,只有当前用户是管理员时,才能显示管理链接。 下面的部分描述 Spring Security 集成的 Servlet 3 方法。 可以使用 HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse) 方法来确保对用户进行身份验证。如果没有对其进行身份验证,则配置的 AuthenticationEntryPoint 将用于请求用户进行身份验证(即重定向到登录页面)。
HttpServletRequest.login(String,String) 方法可用于使用当前 try { httpServletRequest.login("user","password"); } catch(ServletException e) { // fail to authenticate }
HttpServletRequest.logout() 方法可以用来记录当前用户。 通常,这意味着 SecurityContextHolder 将被清除,HttpSession 将被无效,任何 "Remember Me" 身份验证将被清理,等等。然而,配置的 LogoutHandler 实现将根据 Spring Security 配置而变化。重要的是要注意,在调用 HttpServletRequest.logout() 之后,你仍然负责写出响应。通常,这将涉及重定向到欢迎页面。 AsynchContext.start(Runnable) 方法,确保你的凭证将被传播到新线程。使用 Spring Security 的并发支持,Spring Security 覆盖 AsyncContext.start(Runnable),以确保在处理 Runnable 时使用当前的 SecurityContext。例如,下面将输出当前用户的身份验证: final AsyncContext async = httpServletRequest.startAsync(); async.start(new Runnable() { public void run() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); try { final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse(); asyncResponse.setStatus(HttpServletResponse.SC_OK); asyncResponse.getWriter().write(String.valueOf(authentication)); async.complete(); } catch(Exception e) { throw new RuntimeException(e); } } }); 如果你使用的是基于 Java 的配置,那么你已经准备好了。如果使用 XML 配置,则有一些更新是必要的。第一步是确保你已经更新了 web.xml 至少使用 3.0 模式,如下所示: <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> </web-app> 接下来,你需要确保 springSecurityFilterChain 是用于处理异步请求的设置。 <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy </filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ASYNC</dispatcher> </filter-mapping> 就是这样!现在 Spring Security 将确保你的 SecurityContext 也在异步请求上传播。 那么它是如何工作的呢?如果你并不真正感兴趣,请跳过本节的其余部分,否则继续阅读。大部分内容都内置在 Servlet 规范中,但是 Spring Security 做了一些微调,以确保异步请求能够正常工作。在 Spring Security 3.2 之前,一旦提交 HttpServletResponse,来自 SecurityContextHolder 的 SecurityContext 将自动保存。这可能导致异步环境中的问题。例如,考虑以下内容: httpServletRequest.startAsync(); new Thread("AsyncThread") { @Override public void run() { try { // Do work TimeUnit.SECONDS.sleep(1); // Write to and commit the httpServletResponse httpServletResponse.getOutputStream().flush(); } catch (Exception e) { e.printStackTrace(); } } }.start(); 问题是这个线程对于 Spring Security 并不知道,因此 SecurityContext 不被传播到它。这意味着当我们提交 HttpServletResponse 时,没有 SecuriytContext。当 Spring Security 在提交 HttpServletResponse 时自动保存 SecurityContext,它将丢失登录用户。 由于版本 3.2,Spring Security 足够智能,一旦调用 HttpServletRequest.startAsync() 就不会在提交 HttpServletResponse 时自动保存 SecurityContext。 下面的部分描述 Spring Security 集成的 Servlet 3.1 方法。 在 Servlet 3.1 和更高版本中,HttpServletRequest.changeSessionId() 是用于防止会话固定攻击的默认方法。 |