SpringSecurity中的表单认证详细解析
作者:快乐的小三菊
一、默认表单认证:
首先,新建一个 configuration 包用于存放通用配置;然后新建一个 WebSecurityConfig 类,使其继承 WebSecurityConfigurerAdapter ,如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ }
在给WebSecurityConfig 类上加上 @EnableWebSecurity 注解后,便会自动被 Spring 发现并注册(点击@EnableWebSecurity 注解可以看到 @Configuration 注解已经存在,所以此处不需要额外添加)。
我们接着查看WebSecurityConfigurerAdapter 类对 configure(HttpSecurity http)方法的定义。如下所示:
protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }
可以看到WebSecurityConfigurerAdapter 已经默认声明了一些安全特性:
a、验证所有用户请求。
b、允许用户使用表单登录进行身份验证(Spring Security 提供了一个简单的表单登录页面)。
c、允许用户使用 HTTP 基本认证。
现在重启服务,应用新的安全配置。可以预见,在下次访问 localhost:8080 时,系统会要求我们进行表单认证。如下图所示:
在上图中我们可以发现,我们访问的地址自动跳转到 localhost:8080/login ,这正是 Spring Security 的默认登录页,只需要输入正确的用户名和密码便可跳转到回原来的访问地址。
二、自定义表单认证
1、初步配置自定义表单登录页
虽然自动生成的表单登录页可以方便、快速地启动,但是大多数应用程序更希望提供自己的表单登录页,此时就需要覆盖 configure() 方法,如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin(). loginPage("/myLogin.html") // 使登录页不设限访问 .permitAll() .and(). csrf().disable(); } }
2、认识 HttpSecurity
HttpSecurity 实际上对应了 Spring Security 命名空间配置方式中 xml 文件内的标签。允许我们为特定的 http 请求配置安全策略。
在 xml 文件中,声明大量配置早已司空见惯;但在 Java 配置中,按照传统的方式,我们需要这样来调用,如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry urlRegistry=http.authorizeRequests(); ExpressionUrlAuthorizationConfigurer.AuthorizedUrl authorizedUrl = (ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)urlRegistry .anyRequest(); authorizedUrl.authenticated(); // more FormLoginConfigurer<HttpSecurity> formLoginConfigurer = http.formLogin(); formLoginConfigurer.loginPage("/myLogin.html"); formLoginConfigurer.permitAll(); // more } }
可以想象出这是多么烦琐且令人痛苦的一件事。HttpSecurity 首先被设计为链式调用,在执行每个方法后,都会返回一个预期的上下文,便于连续调用。我们不需要关心每个方法究竟返回了什么、如何进行下一个配置等细节。
HttpSecurity 提供了很多配置相关的方法,分别对应命名空间配置中的子标签 <http>。例如,authorizeRequests()、formLogin()、httpBasic() 和csrf() 分别对应 <intercept-url>、<form-login>、<http-basic> 和 <csrf> 标签。调用这些方法之后,除非使用 and() 方法结束当前标签,上下文才会回到 HttpSecurity ,否则链式调用的上下文将自动进入对应的标签域。
authorizeRequests() 方法实际上返回了一个 URL 拦截注册器,我们可以调用它提供的 anyRequest()、antMatchers() 和regexMatchers() 等方法来匹配系统的 URL ,并为其指定安全策略。
formLogin() 和httpBasic() 方法都声明了需要 Spring Security 提供的表单认证方式,分别返回对应的配置器。其中,formLogin.loginPage("/myLogin.html") 指定自定义的登录页为/myLogin.html ,同时,Spring Security 会用 /myLogin.html 注册一个 POST 路由,用于接收登录请求。
csrf() 方法是 Spring Security 提供的跨站请求伪造防护功能,当我们继承WebSecurityConfigurerAdapter 会默认开启 csrf() 方法,关于 csrf() 方法的更多内容会在后面的章节专门探讨,以使测试进程更加顺利。
重新启动服务后在此访问 localhost:8080 ,页面会自动跳转到 localhost:8080/myLogin.html。由于 /myLogin.html 无法定位到页面资源,所以会显示一个 404 页面,如下所示:
3、编写表单登录页
表单登录页myLogin.html的代码如下所示:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <div class = "login" style="width:300px;height:300px"> <h2>Acced Form</h2> <div class ="login-top"></div> <h1>LOGIN FORM</h1> <form action="myLogin.html" method="post"> <input type="text" name="username" placeholder="username"/> <input type="password" name="password" placeholder="password"/> <div class="forgot" style="margin-top:20px;"> <a href="#">forgot Password</a> <input type="submit" value="login"> </div> </form> <div class="login-bottom"> <h3>New User <a href ="">Register</a> </h3> </div> </div> </body> </html>
在表单登录页中,仅有一个表单,用户名和密码分别为 username 和 password,并以 POST 的方式提交到 /myLogin.html。
我们将该文件放置在 resources/static/ 下。重启服务,再次访问 localhost:8080,即可看到自定义的表单登录页,如下所示:
输入正确的用户名和密码后,单击 login 按钮,即可成功跳转。
4、其他表单配置项
在自定义表单登录页之后,处理登录请求的 URL 也会相应改变,如何自定义 URL 呢?很简单, Spring Security 在表单定制里提供了相应的支持,代码如下所示:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") .loginProcessingUrl("/login") .permitAll() .and() .csrf().disable(); } }
此时,有些读者可能会有疑问,因为按照惯例,在发送登录请求并认证成功之后,页面会跳转回原访问页。在某些系统中的确是跳转回原访问页的,但在部分前后端完全分离、仅靠 json完成所有交互的系统中,一般会在登录时返回一段 json数据,告知前端成功登录成功与否,由前端决定如何处理后续逻辑,而非由服务器主动执行页面跳转。这在 Spring Security 中同样可以实现:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") .loginProcessingUrl("/login") .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest arg0, HttpServletResponse arg1, Authentication arg2) throws IOException, ServletException { arg1.setContentType("application/json;charset=UTF-8"); PrintWriter out = arg1.getWriter(); out.write("{\"error_code\":\"0\",\"message\":\"欢迎登录系统\"}"); } }) .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest arg0, HttpServletResponse arg1, AuthenticationException arg2) throws IOException, ServletException { arg1.setContentType("application/json;charset=UTF-8"); arg1.setStatus(401); PrintWriter out = arg1.getWriter(); // 输出失败的原因 out.write("{\"error_code\":\"401\",\"name\":\""+arg2.getCause()+"\"," + "\"message\":\""+arg2.getMessage()+"\"}}"); } }) .and() .csrf().disable(); } }
表单登录配置模块提供了 successHandler() 和 failureHandler() 两个方法,分别处理登录成功和登录失败的逻辑。
其中,successHandler() 方法带有一个 Authentication 参数,携带当前登录用户名及其角色等信息;而 failureHandler() 方法携带一个 AuthenticationException 异常参数。
具体处理方式需按照系统的情况自定义。
在形式上,我们确实使用了 Spring Security 的表单认证功能,并且自定义了表单登录页。但实际上,这还远远不够。
例如,在实际系统中,我们正常登录时使用的用户名和密码都来自数据库,这里却都写在配置上。
更进一步,我们可以对每个登录用户都设定详细的权限,而并非一个通用角色。这些内容将在后面章节讲解。
到此这篇关于SpringSecurity中的表单认证详细解析的文章就介绍到这了,更多相关SpringSecurity表单认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!