在篇文章中,咱们将学习如何使用 Spring 和 Spring Security 5 提供更安全的 RESTful API。
咱们将使用 Java 配置来设置安全性,并将使用登陆和 Cookie 方法进行身份验证。java
Spring Security 的体系结构彻底基于 Servlet 过滤器。
注册 Spring Security 过滤器的最简单选择是添加 @EnableWebSecurity 注解到咱们的 config 类:web
@Config @EnableWebSecurity public class SecurityJavaConfig extends WebSecurityConfigurerAdapter { // ... }
对于非 Spring Boot 应用,咱们能够扩展 AbstractSecurityWebApplicationInitializer 并在其构造函数中传递 config 类:spring
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { public SecurityWebApplicationInitializer() { super(SecurityJavaConfig.class); } }
或者咱们能够在应用的 web.xml 中声明它:shell
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
咱们应该将过滤器命名为 “springSecurityFilterChain”,以匹配容器中 Spring Security 建立的默认 bean。
注意,定义的过滤器不是实现安全逻辑的实际类。相反,它是一个委托 filterproxy,将筛选器的方法委托给内部 bean。这样作是为了让目标 bean 仍然可以从 Spring 上下文的生命周期和灵活性中受益。json
咱们能够彻底在 Java 类中进行安全配置,方法是建立一个扩展了 WebSecurityConfigurerAdapter 的配置类,并用 @EnableWebSecurity 对其进行注释:api
@Configuration @EnableWebSecurity public class SecurityJavaConfig extends WebSecurityConfigurerAdapter { // ... }
如今,让咱们在 SecurityJavaConfig 中建立不一样角色的用户,咱们将使用这些用户来验证咱们的 API 端点:安全
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN") .and() .withUser("user").password(encoder().encode("userPass")).roles("USER"); } @Bean public PasswordEncoder encoder() { return new BCryptPasswordEncoder(); }
接下来,让咱们为咱们的 API 端点配置安全性:cookie
@Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint(restAuthenticationEntryPoint) .and() .authorizeRequests() .antMatchers("/api/foos").authenticated() .antMatchers("/api/admin/**").hasRole("ADMIN") .and() .formLogin() .successHandler(mySuccessHandler) .failureHandler(myFailureHandler) .and() .logout(); }
在咱们的代码实现中,咱们使用 antMatchers 建立安全的映射 /api/foos 和 /api/admin/**。
任何通过身份验证的用户均可以访问 /api/foos。另外一方面,/api/admin/** 只能被 admin 角色用户访问。
在标准的 web 应用程序中,当未经身份验证的客户机试图访问受保护的资源时,身份验证过程可能会自动触发。此过程一般重定向到登陆页面,以便用户能够输入凭据。
然而,对于 REST Web 服务,这种行为没有多大意义。咱们应该可以仅经过对正确URI的请求进行身份验证,若是用户没有通过身份验证,则全部请求都应该失败,并带有401未受权状态码。
Spring Security 使用 Entry Point 的概念处理身份验证过程的自动触发 ——这是配置中必需的一部分,能够经过 authenticationEntryPoint 方法注入。
在触发时简单地返回 401:app
@Component public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence( HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } }
有多种方法能够对 REST API 进行身份验证。 Spring Security 提供的默认值之一是表单登陆,它使用身份验证处理过滤器 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter。
formLogin 元素将建立这个过滤器,并提供额外的方法 successHandler 和 failureHandler 来分别设置咱们的自定义身份验证成功和失败处理程序。curl
默认状况下,表单登陆将使用 301 状态码来回答成功的身份验证请求,这在登陆后须要重定向的实际登陆表单的上下文中是有意义的。
然而,对于 RESTful web 服务,成功验证所需的响应应该是 200 OK。
为此,咱们在表单登陆筛选器中注入一个自定义身份验证成功处理程序,以替换默认的处理程序。新的处理程序实现与默认的 org.springframe .security.web.authentication 彻底相同的登陆。SavedRequestAwareAuthenticationSuccessHandler 有一个明显的区别-它删除了重定向逻辑:
public class MySavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private RequestCache requestCache = new HttpSessionRequestCache(); @Override public void onAuthenticationSuccess( HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest == null) { clearAuthenticationAttributes(request); return; } String targetUrlParam = getTargetUrlParameter(); if (isAlwaysUseDefaultTargetUrl() || (targetUrlParam != null && StringUtils.hasText(request.getParameter(targetUrlParam)))) { requestCache.removeRequest(request, response); clearAuthenticationAttributes(request); return; } clearAuthenticationAttributes(request); } public void setRequestCache(RequestCache requestCache) { this.requestCache = requestCache; } }
身份验证过程使用内存中的提供者来执行身份验证。
咱们建立了两个用户,即具备 USER 角色的 user 和具备 ADMIN 角色的 admin。
如今,让咱们看看如何针对不一样的用户针对REST API进行身份验证。
登陆的 URL 为 /login,一个简单的 curl 命令,用于对名为 user 和密码 userPass 的用户执行登陆。
curl -i -X POST -d username=user -d password=userPass http://localhost:8080/spring-security-rest/login
此请求将返回 Cookie,咱们能够将其用于针对 REST 服务的任何后续请求。
咱们可使用 curl 进行认证,并将它接收到的 cookie 保存在一个文件中:
curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt http://localhost:8080/spring-security-rest/login
而后咱们可使用文件中的 cookie 来作进一步的认证请求:
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt http://localhost:8080/spring-security-rest/api/foos
因为用户能够访问 /api/foos/* 端点,这个通过身份验证的请求将正确地获得一个 200 OK :
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Wed, 24 Jul 2013 20:31:13 GMT [{"id":0,"name":"qulingfeng"}]
相似地,对于 admin 用户,咱们可使用 curl 进行认证:
curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt http://localhost:8080/spring-security-rest/login
而后更新 cookies 访问管理端点 /api/admin/* :
curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt http://localhost:8080/spring-security-rest/api/admin/x
因为管理用户能够访问端点 /api/admin/*,全部响应成功:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 X-Frame-Options: DENY Content-Type: application/json;charset=ISO-8859-1 Content-Length: 5 Date: Mon, 15 Oct 2018 17:16:39 GMT Hello
咱们也能够用 XML 代替 Java 配置来完成以上全部的安全配置:
<http entry-point-ref="restAuthenticationEntryPoint"> <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/> <form-login authentication-success-handler-ref="mySuccessHandler" authentication-failure-handler-ref="myFailureHandler" /> <logout /> </http> <beans:bean id="mySuccessHandler" class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/> <beans:bean id="myFailureHandler" class= "org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/> <authentication-manager alias="authenticationManager"> <authentication-provider> <user-service> <user name="admin" password="adminPass" authorities="ROLE_ADMIN"/> <user name="user" password="userPass" authorities="ROLE_USER"/> </user-service> </authentication-provider> </authentication-manager>
在本教程中,咱们介绍了使用 Spring security 5 实现 RESTful 服务的基本安全配置和实现。
咱们学习了如何彻底经过Java配置为 REST API 进行安全配置,并研究了其 web.xml 配置替代方案。
接下来,咱们讨论了如何为受保护的应用程序建立用户和角色,以及如何将这些用户映射到应用程序的特定端点。
最后,咱们还研究了如何建立自定义身份验证入口点和自定义成功处理程序,以便在控制安全性方面为应用程序提供更好的灵活性。
欢迎关注个人公众号:曲翎风,得到独家整理的学习资源和平常干货推送。
若是您对个人专题内容感兴趣,也能够关注个人博客: sagowiec.com