SpringSecurity自定义Form表单使用方法讲解

目录
  • 背景
  • 实验-HttpBasic
  • 实验-自定义登录页面
  • 实验-自定义登录接口
  • 实验-自定义登录数据参数
  • 实验-自定义登录失败、成功处理器
  • 实验-自定义登录成功跳转页面
  • 实验-自定义退出接口

背景

本系列教程,是作为团队内部的培训资料准备的。主要以实验的方式来体验SpringSecurity的各项Feature。

新建一个SpringBoot项目,起名springboot-security-form,核心依赖为WebSpringSecurityThymeleaf

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>

实验-HttpBasic

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {// There is no PasswordEncoder mapped for the id "null"PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();String yourPassword = "123";System.out.println("Encoded password: " + encoder.encode(yourPassword));// Config account info and permissionsauth.inMemoryAuthentication().withUser("dev").password(encoder.encode(yourPassword)).authorities("p1").and().withUser("test").password(encoder.encode(yourPassword)).authorities("p2");
}
@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().httpBasic();
}

实验-自定义登录页面

登录页面配置

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().csrf().disable() // turn off csrf, or will be 403 forbidden.formLogin() // Support form and HTTPBasic.loginPage("/login");
}

后端登录页面接口

@Controller
public class LoginController {@GetMapping("/login")public String login() {return "login";}@GetMapping(value = "/user/add")@ResponseBodypublic String accessResource1() {return " Access Resource 1: Add User";}@GetMapping(value = "/user/query")@ResponseBodypublic String accessResource2() {return " Access Resource 2: Query User";}
}

前端模板

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Login</title>
</head>
<body>
<form action="login" method="post"><span>用户名</span><input type="text" name="username" /> <br><span>密码</span><input type="password" name="password" /> <br><input type="submit" value="登录">
</form>
</body>
</html>

Note:此时,需要先关闭CSRF,.csrf().disable(),否则报403;

实验-自定义登录接口

默认登录页面接口与登录数据提交接口是同一个:/login,顺着.loginPage,进入FormLoginConfigurer,源码如下:

@Override
public FormLoginConfigurer<H> loginPage(String loginPage) {return super.loginPage(loginPage);
}

继续进入父类的loginPage方法,

protected T loginPage(String loginPage) {setLoginPage(loginPage);updateAuthenticationDefaults();this.customLoginPage = true;return getSelf();
}

继续跟踪进入方法updateAuthenticationDefaults();,可以看到,如果没有配置loginProcessingUrl,那么loginProcessingUrlloginPage便相同。

protected final void updateAuthenticationDefaults() {if (loginProcessingUrl == null) {loginProcessingUrl(loginPage);}if (failureHandler == null) {failureUrl(loginPage + "?error");}final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(LogoutConfigurer.class);if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");}
}

下面我们自定义登录数据提交接口为/formLogin,此时相应的前端action也要修改。

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().csrf().disable() // turn off csrf, or will be 403 forbidden.formLogin() // Support form and HTTPBasic.loginPage("/login").loginProcessingUrl("/formLogin");
}
<form action="formLogin" method="post"><span>用户名</span><input type="text" name="username" /> <br><span>密码</span><input type="password" name="password" /> <br><input type="submit" value="登录">
</form>

实验-自定义登录数据参数

前面我们自定义了登录页面,在form表单中设置用户名、密码分别为username, password,那为什么这样写呢,可以改成别的嘛?可以倒是可以,但是不能随便改;

如果这里我们把username改为name,再次尝试登录,后端接口将报错:org.springframework.security.authentication.BadCredentialsException: Bad credentials。。可是实际项目中我们的用户名密码就是不叫这个名字呢?我们可以进行配置.usernameParameter("name")

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().csrf().disable() // turn off csrf, or will be 403 forbidden.formLogin() // Support form and HTTPBasic.loginPage("/login").loginProcessingUrl("/formLogin").usernameParameter("name");
}
<form action="formLogin" method="post"><span>用户名</span><input type="text" name="name" /> <br><span>密码</span><input type="password" name="password" /> <br><input type="submit" value="登录">
</form>

默认的用户名、密码分别为username, password,我们看下SpringSecurity的源码:

public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
		AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
	/**
	 * Creates a new instance
	 * @see HttpSecurity#formLogin()
	 */
	public FormLoginConfigurer() {
		super(new UsernamePasswordAuthenticationFilter(), null);
		usernameParameter("username");
		passwordParameter("password");
	}
}

实验-自定义登录失败、成功处理器

问题:就以上个实验3中的报错信息为例,或当用户名、密码输错后,如何在后台看到错误信息?

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().csrf().disable() // turn off csrf, or will be 403 forbidden.formLogin() // Support form and HTTPBasic.loginPage("/login").loginProcessingUrl("/formLogin").usernameParameter("name").failureHandler(new AuthenticationFailureHandler(){@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {exception.printStackTrace();request.getRequestDispatcher(request.getRequestURL().toString()).forward(request, response);}});
}

常见的认证异常,这里可以看到AuthenticationException共有18个子类:

上述增加了在认证失败时的处理:输出错误信息。同理,如果想在登录成功时直接进行一些处理(eg: 数据初始化等),可以使用以下配置:

.successHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,Authentication authentication) throws IOException, ServletException {System.out.println("Login Successfully~");// do something here: initial work or forward to different url regarding different roles...request.getRequestDispatcher("").forward(request, response);}
})

实验-自定义登录成功跳转页面

经历千难万险,终于要登录成功了。进来之后要跳转到哪里呢?看你喽~想跳哪里跳哪里。。

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().csrf().disable() // turn off csrf, or will be 403 forbidden.formLogin() // Support form and HTTPBasic.loginPage("/login").loginProcessingUrl("/formLogin").usernameParameter("name").failureHandler(new AuthenticationFailureHandler(){@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {exception.printStackTrace();request.getRequestDispatcher(request.getRequestURL().toString()).forward(request, response);}}).successForwardUrl("/ok"); // custom login success page, a POST request
}
@Controller
public class LoginController {...@PostMapping(value = "/ok")@ResponseBodypublic String ok() {return "ok";}
}

通过.successForwardUrl("/ok")配置了登录成功之后要跳转的页面路径或接口,同时需要在后端新增/ok接口。

Note:

  • 注意这里successForwardUrl的接口必须为POST接口;
  • 除了.successForwardUrl("/ok");,还可以使用.defaultSuccessUrl("/ok");或者.defaultSuccessUrl("/ok", true); 第二个参数true表示不管是从哪个地址进来,登录后全部跳转到指定的地址,此时与successForwardUrl效果相同,默认为false
  • 当然,除了登录成功后的跳转,还有登录失败后的跳转:failureForwardUrl

实验-自定义退出接口

默认的退出接口是/logout,可进行配置:

@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/add").hasAuthority("p1").antMatchers("/user/query").hasAuthority("p2").antMatchers("/user/**").authenticated().anyRequest().permitAll() // Let other request pass.and().csrf().disable() // turn off csrf, or will be 403 forbidden.formLogin() // Support form and HTTPBasic.loginPage("/login").loginProcessingUrl("/formLogin").usernameParameter("name").failureHandler(new AuthenticationFailureHandler(){@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {exception.printStackTrace();request.getRequestDispatcher(request.getRequestURL().toString()).forward(request, response);}}).successForwardUrl("/ok") // custom login success page, a POST request.and().logout().logoutUrl("/leave");}

上述配置将退出接口改为/leave。在默认的退出过程中,还做了诸如清除认证信息和使Session失效等工作:

public class SecurityContextLogoutHandler implements LogoutHandler {
	protected final Log logger = LogFactory.getLog(this.getClass());
	private boolean invalidateHttpSession = true;
	private boolean clearAuthentication = true;
	// ~ Methods
	// ==========================================
	/**
	 * Requires the request to be passed in.
	 *
	 * @param request from which to obtain a HTTP session (cannot be null)
	 * @param response not used (can be <code>null</code>)
	 * @param authentication not used (can be <code>null</code>)
	 */
	public void logout(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) {
		Assert.notNull(request, "HttpServletRequest required");
		if (invalidateHttpSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				logger.debug("Invalidating session: " + session.getId());
				session.invalidate();
			}
		}
		if (clearAuthentication) {
			SecurityContext context = SecurityContextHolder.getContext();
			context.setAuthentication(null);
		}
		SecurityContextHolder.clearContext();
	}
} 

到此这篇关于SpringSecurity自定义Form表单使用方法讲解的文章就介绍到这了,更多相关SpringSecurity Form表单内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Security中防护CSRF功能详解

    目录 CSRF是什么? CSRF可以做什么? CSRF的原理 Spring Security解决方案 Spring Security实现防护csrf的原理 总结 CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF. CSRF可以做什么? 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求. CSRF能够做的事情包括:

  • SpringBoot Security密码加盐实例

    目录 修改加密和验证方法 自定义 DaoAuthenticationProvider 注册到ProciderManager中 修改加密和验证方法 /** * 生成BCryptPasswordEncoder密码 * * @param password 密码 * @param salt 盐值 * @return 加密字符串 */ public static String encryptPassword(String password,String salt) { BCryptPasswordEnco

  • Spring Boot中的SpringSecurity基础教程

    目录 一 SpringSecurity简介 二 实战演示 0. 环境 介绍 1. 新建一个初始的springboot项目 2. 导入thymeleaf依赖 3. 导入静态资源 4. 编写controller跳转 5. 认证和授权 6. 权限控制和注销 7. 记住登录 8. 定制登录页面 三 完整代码 3.1 pom配置文件 3.2 RouterController.java 3.3 SecurityConfig.java 3.4 login.html 3.5 index.html 3.6 效果展

  • SpringSecurity自定义Form表单使用方法讲解

    目录 背景 实验-HttpBasic 实验-自定义登录页面 实验-自定义登录接口 实验-自定义登录数据参数 实验-自定义登录失败.成功处理器 实验-自定义登录成功跳转页面 实验-自定义退出接口 背景 本系列教程,是作为团队内部的培训资料准备的.主要以实验的方式来体验SpringSecurity的各项Feature. 新建一个SpringBoot项目,起名springboot-security-form,核心依赖为Web,SpringSecurity与Thymeleaf. <dependencie

  • 用JavaScrip正则表达式验证form表单的方法

    document:标签之间 location:url history:前进后退 <html> <head> <script type="text/javascript"> function show() { //弹出一个提示框 window.alert("hh"); } //将show方法绑定到按钮上 window.onload=function() { //定位到按钮 var buttonElement=document.for

  • 解决antd Form 表单校验方法无响应的问题

    antd 的 表单校验方法包括 validateFields 和 validateFieldsAndScroll 里面可以接收校验字段数组, options, 和一个回调函数 from.validateFields([name, age], {}, (err, val)=> {}) 校验全部表单数据 from.validateFields((err, val)=> {}) // 无响应 发现无响应, 无报错, 完全蒙逼了, 排查良久, 添加校验字段后发现可以成功校验, 于是想着大不了我全部手动

  • js实现a标签超链接提交form表单的方法

    本文实例讲述了js实现a标签超链接提交form表单的方法.分享给大家供大家参考.具体实现方法如下: <form action="/home/search" method="get" id="search_form"> <div class="searchBox png" id="searchBox"> <input type="text" id="

  • php实现跨域提交form表单的方法【2种方法】

    本文实例讲述了php实现跨域提交form表单的方法.分享给大家供大家参考,具体如下: 有时我们为了网站安全考虑,我们不允许直接跨域提交form表单数据,如果我们自己有这个需求呢?下面我们来介绍两种跨域的方法解决直接跨域问题. 下面我们来看看两种php跨域提交form的方法 一.通过php curl function curlPost($url,$params) { $postData = ''; foreach($params as $k => $v) { $postData .= $k . '

  • HTML form表单提交方法案例详解

    form表单提交方式总结一下: 一.利用submit按钮实现提交,当点击submit按钮时,触发onclick事件,由JavaScript里函数判断输入内容是否为空,如果为空,返回false, 不提交,如果不为空,提交到由action指定的地址. <script type="text/javascript"> function check(form) { if(form.userId.value=='') { alert("请输入用户帐号!"); for

  • JSON生成Form表单的方法示例

    JSON表单描述 JSON表单是一个基于React的抽象组件,它可以把JSON数据格式描述的表单转换成项目中的表单,它可以用简短的几行代码,快速的生成Form表单. JSON表单的优点是: 可以快速构建出一个表单 表单的数据.逻辑.视图分离,方便抽离和进一步抽象 提供校验.自动缓存等额外功能,提升录入体验 可以跨项目的共用复杂的表单组件 原始表单的缺点 1:代码量庞大,开发效率低 每次开发一个表单页的时候,都需要重复编写表单组件及其交互事件的代码,这块代码重复编写且与主线业务逻辑无关,除此之外,

  • 对于Form表单reset方法的新认识

    HTML中Form表单的reset方法被用来清空用户所输入的内容,以前一直误以为其是单纯的将input等输入项中的值清空. 但实际上不是这样的,reset方法的本质是将input等输入项中的内容还原为属性value中的值,而不是""空值. w3c上是这样说的: 在 HTML 表单中 <input type="reset"> 标签每出现一次,一个 Reset 对象就会被创建. 当重置按钮被点击,包含它的表单中所有输入元素的值都重置为它们的默认值.默认值由

  • jquery实现ajax提交form表单的方法总结

    方法一: 复制代码 代码如下: function AddHandlingFeeToRefund() {            var AjaxURL= "../OrderManagement/AjaxModifyOrderService.aspx";                   alert($('#formAddHandlingFee').serialize());                $.ajax({                    type: "P

  • jQuery ajax提交Form表单实例(附demo源码)

    本文实例讲述了jQuery ajax提交Form表单的方法.分享给大家供大家参考,具体如下: Jquery的$.ajax方法可以实现ajax调用,要设置url,post,参数等. 如果要提交现有Form需要写很多代码,何不直接将Form的提交直接转移到ajax中呢. 以前的处理方法 如Form代码如下: <form id="Form1" action="action.aspx" method="post" > 名称:<input

随机推荐