Spring Security 提供了许多与 Spring MVC 的可选集成。本节进一步详细介绍了集成。
为了使 Spring Security 与 Spring MVC 集成,在你的配置中添加
Spring Security 提供了与 Spring MVC 如何与
为了使用
对于 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/*.xml</param-value> </context-param> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- Load from the ContextLoaderListener --> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
在 public class SecurityInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[] { RootConfiguration.class, WebMvcConfiguration.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
考虑一个映射如下的控制器: @RequestMapping("/admin") public String admin() {
如果我们想限制对这个控制器方法的访问以管理用户,开发人员可以通过以下方式在 protected configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin").hasRole("ADMIN"); } 或者用 XML <http> <intercept-url pattern="/admin" access="hasRole('ADMIN')"/> </http>
无论是哪种配置,URL
问题是我们的安全规则仅仅是保护
相反,我们可以利用 Spring Security 的 protected configure(HttpSecurity http) throws Exception { http .authorizeRequests() .mvcMatchers("/admin").hasRole("ADMIN"); } 或者用 XML <http request-matcher="mvc"> <intercept-url pattern="/admin" access="hasRole('ADMIN')"/> </http>
Spring Security 提供了 <mvc:annotation-driven> <mvc:argument-resolvers> <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
一旦正确配置了
考虑一种情况,其中自定义 @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); CustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal(); // .. find messages for this user and return them ... } 在 Spring Security 3.2 中,我们可以通过添加注解来更直接地解决参数。例如: import org.springframework.security.core.annotation.AuthenticationPrincipal; // ... @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) { // .. find messages for this user and return them ... }
有时可能有必要在某种程度上改变主体。例如,如果 public class CustomUserUserDetails extends User { // ... public CustomUser getCustomUser() { return customUser; } }
然后,我们可以使用一个使用 import org.springframework.security.core.annotation.AuthenticationPrincipal; // ... @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = "customUser") CustomUser customUser) { // .. find messags for this user and return them ... } 我们也可以在我们的 SpEL 表达式中引用 bean。例如,如果我们使用 JPA 来管理用户,并且希望修改和保存当前用户的属性,则可以使用以下内容。 import org.springframework.security.core.annotation.AuthenticationPrincipal; // ... @PutMapping("/users/self") public ModelAndView updateName(@AuthenticationPrincipal(expression = "@jpaEntityManager.merge(#this)") CustomUser attachedCustomUser, @RequestParam String firstName) { // change the firstName on an attached instance which will be persisted to the database attachedCustomUser.setFirstName(firstName); // ... }
通过将
@Target({ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @AuthenticationPrincipal public @interface CurrentUser {}
既然已经指定了 @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) { // .. find messages for this user and return them ... }
Spring Web MVC 3.2+ 对异步请求处理有极好的支持。如果没有其他配置,Spring Security 将自动将 @RequestMapping(method=RequestMethod.POST) public Callable<String> processUpload(final MultipartFile file) { return new Callable<String>() { public Object call() throws Exception { // ... return "someView"; } }; }
这没有一个自动集成,而是由控制器返回的 Spring Security 将自动在使用 Spring MVC 表单标签 的表单中包含 CSRF 令牌。例如,下面的 JSP: <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" version="2.0"> <jsp:directive.page language="java" contentType="text/html" /> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <!-- ... --> <c:url var="logoutUrl" value="/logout"/> <form:form action="${logoutUrl}" method="post"> <input type="submit" value="Log out" /> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </form:form> <!-- ... --> </html> </jsp:root> 将输出与以下类似的 HTML: <!-- ... --> <form action="/context/logout" method="post"> <input type="submit" value="Log out"/> <input type="hidden" name="_csrf" value="f81d4fae-7dec-11d0-a765-00a0c91e6bf6"/> </form> <!-- ... -->
Spring Security 提供了
一旦 @RestController public class CsrfController { @RequestMapping("/csrf") public CsrfToken csrf(CsrfToken token) { return token; } }
重要的是要保持 |