Spring Security登录表单配置示例详解

目录
  • Spring Security登录表单配置
    • 1.引入pom依赖
    • 2.bootstrap.yml添加配置
    • 3.创建login.html
    • 4.创建配置类
    • 5.配置细节
    • 6.登陆成功
    • 7.登陆失败
    • 8.注销登录

Spring Security登录表单配置

1.引入pom依赖

​ 创建一个Spring Boot工程,引入WebSpring Security依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>

    <groupId>com.kapcb.ccc</groupId>
    <artifactId>springsecurity-helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

</project>

2.bootstrap.yml添加配置

​ 工程创建完成之后,为了方便测试,需要在bootstrap.yml配置文件中添加如下配置,将登录用户名和密码固定下来:

spring:
  security:
    user:
      name: kapcb
      password: 123456

3.创建login.html

​ 在resources/static目录下创建login.html页面,这个就是自定义登录页面:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/loginSystem" method="post">
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>

</body>
</html>

​ 这个login.html核心内容就是一个登陆表单,登陆表单中有三个需要注意的地方:

  • form标签中的action熟悉,这里是/loginSystem,表示表单要提交请求到/loginSystem接口上。
  • 用户名框的name属性值为username
  • 密码框的name属性为password

4.创建配置类

​ 定义好login.html之后,接下来就定义两个测试接口,作为受保护资源。当用户登陆成功后,就可以访问受保护的资源。接口定义如下:

package com.kapcb.security.helloworld.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <a>Title: HelloWorldController </a>
 * <a>Author: Kapcb <a>
 * <a>Description: HelloWorldController <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/12 22:05
 * @since 1.0
 */
@RestController
public class HelloWorldController {

    @GetMapping("index")
    public String index() {
        return "login success!";
    }

    @GetMapping("hello")
    public String hello() {
        return "Hello, World!";
    }

}

​ 最后再提供Spring Security的配置类,需要注意的是在Spring Security 5.7版本中已经废弃WebSecurityConfigurerAdapter

package com.kapcb.security.helloworld.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

/**
 * <a>Title: SecurityConfiguration </a>
 * <a>Author: Kapcb <a>
 * <a>Description: SecurityConfiguration <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/13 22:23
 * @since 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .defaultSuccessUrl("/index")
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

}

​ 这里直接使用声明SecurityFilterChain类型的Bean的方式即可配置Spring Security登陆表单。

  • authorizeHttpRequests()方法表示开启权限配置。
  • anyRequest().authenticated():表示所有请求都需要经过认证。
  • and():方法会返回httpSecurityBuilder对象的一个子类,实际上就是HttpSecurity。所以and()方法相当于返回一个HttpSecurity实例,重新开启新一轮配置。
  • formLogin():表示开启表单登陆配置。
  • loginPage("/login.html"):用于配置默认登录页的请求地址。
  • loginProcessingUrl("/loginSystem"):用于配置登录请求接口地址。
  • defaultSuccessUrl("/index"):表示登录请求处理成功后的跳转地址。
  • failureUrl("/login.html"):表示登陆失败跳转的地址。
  • usernameParameter("username"):表示登陆用户名的参数名称。
  • passwordParameter("password"):表示登录密码的参数名称。
  • permitAll():表示跟登录相关的页面和接口不做拦截,直接允许访问。
  • csrf().disable():表示禁用CSRF防御功能。Spring Security自带了CSRF防御机制。为了测试方便,这里先关闭了。

​ 需要注意的是,上面的loginPage()loginProcessingUrl()defaultSuccessUrl()usernameParameter()passwordParameter()需要和之前创建的login.html中登录表单的配置一致。

​ 完成上述配置之后,启动工程,访问http://localhost:9096/index,会自动跳转到http://localhost:9096/login.html页面。输入之前设置好的用户名密码,登陆成功之后,就可以访问到/index页面了。

5.配置细节

​ 在前面的配置中,使用defaultSuccessUrl()方法配置了用户登陆成功之后的跳转地址。使用failureUrl()方法配置了用户登录失败之后的跳转地址。关于用户登录成功与登陆失败,除了这两个方法可以进行配置之外,还有另外两个方法也可配置。

6.登陆成功

​ 当用户登录成功之后,除了使用defaultSuccessUrl()方法可以实现登录成功后的跳转之外,successForwardUrl()方法也可以配置实现登录成功之后的跳转,配置代码如下:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .successForwardUrl("/index")
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }
}

defaultSuccessUrl()successForwardUrl()方法配置的区别如下:

  • defaultSuccessUrl()方法表示当用户登陆成功之后,会自动重定向到登录之前用户访问的地址上。如果用户本身就是直接访问的登陆页面,则登录成功之后就会重定向到defaultSuccessUrl()指定的页面。
  • successForwardUrl()方法则不会考虑用户登录之前访问的地址,只要用户登陆成功,就会通过服务器端跳转到successForwardUrl()所指定的页面。
  • defaultSuccessUrl()方法有一个重载方法,如果重载方法的第二个参数传入true,则defaultSuccessUrl()方法的效果与successForwardUrl()方法类似,即不考虑用户之前的访问地址,只要登陆成功,就重定向到defaultSuccessUrl()所指定的页面。不同之处在于,defaultSuccessUrl()方法是通过重定向实现的客户端跳转,successForwardUrl()则是通过服务端跳转实现的。

​ 无论是defaultSuccessUrl()还是successForwardUrl()方法配置登录成功跳转页面,最终所配置的都是AuthenticationSuccessHandler接口的实例。

​ 在Spring Security中专门提供了AuthenticationSuccessHandler接口来处理用户登陆成功事项,AuthenticationSuccessHandler接口源码如下:

public interface AuthenticationSuccessHandler {
    default void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        this.onAuthenticationSuccess(request, response, authentication);
        chain.doFilter(request, response);
    }

    void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
}

AuthenticationSuccessHandler接口中定义了两个方法,其中一个是default方法,此方法是Spring Security 5.2版本中添加进来的,在处理特定的认证请求Authentication Filter中会用到。另外一个非default方法,则用来处理登陆成功的具体事项,其中Authentication参数保存了登陆成功的用户信息。

AuthenticationSuccessHandler接口在Spring Security源码中一共有三个实现类:

  • SimpleUrlAuthenticationSuccessHandler继承自AbstractAuthenticationTargetUrlRequestHandler。通过AbstractAuthenticationTargetUrlRequestHandler中的handler()方法实现请求重定向。
  • SavedRequestAwareAuthenticationSuccessHandler在SimpleUrlAuthenticationSuccessHandler的基础上增加了请求缓存的功能,可以记录用户登陆之前请求的地址,进而在登陆成功之后重定向到一开始访问的地址。
  • ForwardAuthenticationSuccessHandler的实现比较简单,就是一个服务端跳转。

​ 通过defaultSuccessUrl()方法来配置登陆成功之后重定向的请求地址时,实际上对应的实现类就是SavedRequestAwareAuthenticationSuccessHandlerSavedRequestAwareAuthenticationSuccessHandler源码如下:

public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private RequestCache requestCache = new HttpSessionRequestCache();

    public SavedRequestAwareAuthenticationSuccessHandler() {
    }

    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        SavedRequest savedRequest = this.requestCache.getRequest(request, response);
        if (savedRequest == null) {
            super.onAuthenticationSuccess(request, response, authentication);
        } else {
            String targetUrlParameter = this.getTargetUrlParameter();
            if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
                this.clearAuthenticationAttributes(request);
                String targetUrl = savedRequest.getRedirectUrl();
                this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
            } else {
                this.requestCache.removeRequest(request, response);
                super.onAuthenticationSuccess(request, response, authentication);
            }
        }
    }

    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

SavedRequestAwareAuthenticationSuccessHandler中的核心方法就是onAuthenticationSuccess()

  • 从RequestCache中获取缓存下来的请求,如果没有获取到缓存的请求地址,就代表用户在登录之前并没有访问其它页面。此时直接调用父类的onAuthenticationSuccess()方法来处理,最终重定向到defaultSuccessUrl()方法指定的地址。
  • 接下里会获取一个targetUrlParameter,这个地址是用户显示指定的、希望登录成功之后重定向的地址。例如用户发送的登录请求是http://127.0.0.1:9096/loginSystem?target=/hello,这就表示当用户登陆成功之后,希望主动重定向到/hello接口。targetUrlParameter()方法就是获取重定向地址参数的key,就是上面举例中的target。获取到target之后就可以获取到重定向的地址了。
  • 如果targetUrlParameter存在,或者开发者设置了isAlwaysUseDefaultTargetUrl()为true,此时缓存下来的请求就失去了意义。此时会直接移除掉缓存的请求地址,直接调用父类的onAuthenticationSuccess()方法完成重定向。targetUrlParameter存在,则直重定向到targetUrlParameter指定的地址。isAlwaysUseDefaultTargetUrl为true,则直接重定向到defaultSuccessUrl指定的地址。如果targetUrlParameter存在并且isAlwaysUseDefaultTargetUrl为true,则直接重定向到defaultSuccessUrl指定的地址。
  • 如果上述条件均不满足,那么最终会从缓存请求SavedRequest对象中获取重定向地址,然后进行重定向操作。

​ 这就是SavedRequestAwareAuthenticationSuccessHandler的实现逻辑,开发者也可配置自己的SavedRequestAwareAuthenticationSuccessHandler,如下:

package com.kapcb.security.helloworld.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

/**
 * <a>Title: SecurityConfiguration </a>
 * <a>Author: Kapcb <a>
 * <a>Description: SecurityConfiguration <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/13 22:23
 * @since 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .successHandler(successHandler())
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected SavedRequestAwareAuthenticationSuccessHandler successHandler() {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl("/index");
        successHandler.setTargetUrlParameter("target");
        return successHandler;
    }

}

​ 在上面的配置代码中,制定了targetUrlParametertarget,这样用户就可以在登录请求中,通过target来指定跳转地址。修改一下上面的login.html中的form表单:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/loginSystem?target=/hello" method="post">
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>

</body>
</html>

​ 修改form表单的action属性为/loginSystem?target=/hello。当用户登陆成功之后,就始终会跳转到/hello接口了。

​ 在使用successForwardUrl()方法设置登陆成功后重定向的地址时,实际上对应的实现类是ForwardAuthenticationSuccessHandlerForwardAuthenticationSuccessHandler类的源码非常简单,就是一个服务端转发,ForwardAuthenticationSuccessHandler源码如下:

public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private final String forwardUrl;

    public ForwardAuthenticationSuccessHandler(String forwardUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> {
            return "'" + forwardUrl + "' is not a valid forward URL";
        });
        this.forwardUrl = forwardUrl;
    }
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        request.getRequestDispatcher(this.forwardUrl).forward(request, response);
    }
}

​ 主要功能就是调用request.getRequestDispatcher(this.forwardUrl).forward(request, response)方法实现服务端请求转发。

​ 启动服务之后访问登录页面,登录成功后即可自动重定向到/hello接口。

AuthenticationSuccessHandler默认的三个实现类,无论是哪一种,都是用来处理页面跳转的。随着前后端分离架构的盛行,页面跳转并不能满足我们的业务需求,用户登录成功后,后端返回JSON数据给前端即可。告诉前端当前用户是登陆成功还是失败即可,前端拿到后端响应结果后自行处理即可。像这种需求,可以使用自定义AuthenticationSuccessHandler的实现类完成。

package com.kapcb.security.helloworld.handler;

import com.alibaba.fastjson.JSON;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * <a>Title: CustomizeAuthenticationSuccessHandler </a>
 * <a>Author: Kapcb <a>
 * <a>Description: CustomizeAuthenticationSuccessHandler <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/16 23:47
 * @since 1.0
 */
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String, Object> resultMap = new HashMap<>(4);
        resultMap.put("code", 200);
        resultMap.put("msg", null);
        resultMap.put("data", "1111111");
        String jsonResult = JSON.toJSONString(resultMap);
        response.getWriter().write(jsonResult);
        response.getWriter().close();
    }

}

​ 修改配置类:

package com.kapcb.security.helloworld.configuration;

import com.kapcb.security.helloworld.handler.CustomizeAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

/**
 * <a>Title: SecurityConfiguration </a>
 * <a>Author: Kapcb <a>
 * <a>Description: SecurityConfiguration <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/13 22:23
 * @since 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureUrl("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }
}

​ 在使用自定义AuthenticationSuccessHandler的实现类来完成登录成功之后的处理逻辑之后,还需要同步修改login.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/loginSystem" method="post">
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>

</body>
</html>

​ 将form表单中的action属性修改为之前的/loginSystem。因为此时使用的是自定义的AuthenticationSuccessHandler。逻辑与SavedRequestAwareAuthenticationSuccessHandler是不一样的。

​ 所有的改动完成之后,重启工程。当用户登录成功之后,就不会在进行页面跳转了,而是返回了一段JSON字符串到页面上。

7.登陆失败

Spring Security登陆失败的处理逻辑。为了方便在前端页面展示登录失败的异常信息,首先在项目的pom.xml文件中引入thymeleaf模板引擎的依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

​ 在resources目录下创建templates文件夹,新建loginTemplate.html文件:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>loginTemplate</title>
</head>
<body>
<form action="/loginSystem" method="post">
    <div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>
    <label>username: <input type="text" name="username"/></label></br>
    <label>password: <input type="password" name="password"/></label></br>
    <input type="submit" name="submit" value="login">
</form>
</body>
</html>

loginTemplate.html文件和前面的login.html文件基本类似。login.htmlstatic目录下,属于静态页面,loginTemplate.htmltemplate模板页面。loginTemplate.htmlform标签内新增了<div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>标签,用于展示Spring Security在处理登陆失败时的异常信息。在Spring Security中,用户登陆失败时,异常信息会放在request中返回给前端,开发者可将其直接提取出来展示。

loginTemplate.html属于动态页面,所以就不能像访问static/login.html那样直接访问,需要后端为其提供访问控制器:

package com.kapcb.security.helloworld.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * <a>Title: RouterController </a>
 * <a>Author: Kapcb <a>
 * <a>Description: RouterController <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2022/6/17 23:32
 * @since 1.0
 */
@Controller
@RequestMapping("/router")
public class RouterController {

    @RequestMapping("loginTemplate")
    public String loginTemplate() {
        return "loginTemplate";
    }
}

​ 最后在Spring Security配置类中配置登陆页面:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureUrl("/router/loginTemplate")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }
}

failureUrl()表示登陆失败后重定向到/router/loginTemplate接口请求地址,也就是loginTemplate.html页面。重定向是一种客户端跳转,重定向不方便携带请求失败的异常信息,只能放在URL中。

​ 如果希望在前端展示请求失败的异常信息,可以使用下面这种方式:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureForwardUrl("/router/loginTemplate")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }
}

failureForwardUrl()方法从名字上就可以看出,这种跳转是一种服务器端跳转。服务器端跳转的好处是可以携带登录异常信息。如果用户登陆失败,自动跳转回登录页面后,就可以将错误信息展示出来。

​ 无论是failureUrl()还是failureForwardUrl()方法,最终所配置的都是AuthenticationFailureHandler接口的实现类。Spring Security中提供了AuthenticationFailureHandler接口,为登陆失败下的处理方式提供顶级拓展。AuthenticationFailureHandler源码如下:

public interface AuthenticationFailureHandler {
    void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException;
}

AuthenticationFailureHandler接口中只定义了一个onAuthenticationFailure()方法,用于处理登陆失败的请求。AuthenticationException表示登陆失败的异常信息。Spring Security中为AuthenticationFailureHandler一共提供了五个实现类:

  • SimpleUrlAuthenticationFailureHandler:默认的处理逻辑就是通过重定向跳转到登陆页面,当然也可以通过配置forwardToDestination属性将重定向改为服务器端跳转,failureUrl()方法底层实现逻辑就是SimpleUrlAuthenticationFailureHandler。
  • ExceptionMappingAuthenticationFailureHandler:可以实现根据不同异常类型,映射到不同路径。
  • ForwardAuthenticationFailureHandler:表示通过服务器端跳转来重新回到登陆页面,failureForwardUrl()方法底层实现逻辑就是ForwardAuthenticationFailureHandler。
  • AuthenticationEntryPointFailureHandler:是Spring Security 5.2新引入的处理类,可以通过AuthenticationEntryPoint来处理登陆异常。
  • DelegatingAuthenticationFailureHandler:可以实现为不同的异常类型配置不同的登录失败处理回调。

​ 举个简单的例子。假设不使用failureForwardUrl()方法进行登陆失败的处理逻辑配置,同时又想在登陆失败后通过服务器端跳转回到登陆页面,那么可以自定义SimpleUrlAuthenticationFailureHandler配置,并将forwardToDestination属性设置为true,代码如下:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(failureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected SimpleUrlAuthenticationFailureHandler failureHandler() {
        SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
        failureHandler.setDefaultFailureUrl("/loginFail");
        failureHandler.setUseForward(true);
        return failureHandler;
    }

}

​ 这样配置之后,用户再次登陆失败,就会通过服务端跳转重新回到登陆页面,同时页面上也会展示相应的错误信息,效果和failureForwardUrl()一样。

SimpleUrlAuthenticationFailureHandler的源码也很简单:

public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private String defaultFailureUrl;
    private boolean forwardToDestination = false;
    private boolean allowSessionCreation = true;
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public SimpleUrlAuthenticationFailureHandler() {
    }

    public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
        this.setDefaultFailureUrl(defaultFailureUrl);
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (this.defaultFailureUrl == null) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Sending 401 Unauthorized error since no failure URL is set");
            } else {
                this.logger.debug("Sending 401 Unauthorized error");
            }

            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        } else {
            this.saveException(request, exception);
            if (this.forwardToDestination) {
                this.logger.debug("Forwarding to " + this.defaultFailureUrl);
                request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
            } else {
                this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
            }

        }
    }

    protected final void saveException(HttpServletRequest request, AuthenticationException exception) {
        if (this.forwardToDestination) {
            request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
        } else {
            HttpSession session = request.getSession(false);
            if (session != null || this.allowSessionCreation) {
                request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
            }

        }
    }

    public void setDefaultFailureUrl(String defaultFailureUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl), () -> {
            return "'" + defaultFailureUrl + "' is not a valid redirect URL";
        });
        this.defaultFailureUrl = defaultFailureUrl;
    }

    protected boolean isUseForward() {
        return this.forwardToDestination;
    }

    public void setUseForward(boolean forwardToDestination) {
        this.forwardToDestination = forwardToDestination;
    }

    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }

    protected RedirectStrategy getRedirectStrategy() {
        return this.redirectStrategy;
    }

    protected boolean isAllowSessionCreation() {
        return this.allowSessionCreation;
    }

    public void setAllowSessionCreation(boolean allowSessionCreation) {
        this.allowSessionCreation = allowSessionCreation;
    }
}

SimpleUrlAuthenticationFailureHandler提供了无参和有参构造器。使用有参构造器构造SimpleUrlAuthenticationFailureHandler对象时,可传入defaultFailureUrldefaultFailureUrl也就是登陆失败时要跳转的地址。

onAuthenticationFailure()方法中,首先会判断defaultFailureUrl是否为null,如果为null则直接通过response返回401状态码Unauthorized

​ 如果defaultFailureUrl不为空,则调用saveException()方。在saveException()方法中,如果forwardToDestination属性为true,表示此时需要通过服务器端跳转回登录首页,此时就将异常信息放到request中。在回到onAuthenticationFailure()方法中,如果forwardToDestination属性为true,就通过服务器端跳转回到登录页面,否则通过重定向回到登陆页面。

​ 如果是前后端分离开发,登陆失败时就不需要进行页面跳转了,只需要返回登录失败的JSON响应给前端即可。这种场景下通过AuthenticationFailureHandler的实现类来完成,实现代码如下:

public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        Map<String, Object> resultMap = new HashMap<>(4);
        resultMap.put("code", 500);
        resultMap.put("msg", exception.getMessage());
        resultMap.put("data", null);
        String jsonResult = JSON.toJSONString(resultMap);
        response.getWriter().write(jsonResult);
        response.getWriter().close();
    }

}

​ 在Spring Security配置类中配置自定义登陆失败处理器:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

​ 配置完成后,当用户再次登陆失败,就不会进行页面跳转了,而是直接返回JSON字符串。

8.注销登录

Spring Security中提供了默认的注销页面,开发者也可以根据自己的需求对注销登录进行定制。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}
  • logout():表示开启注销登录配置。
  • logoutUrl():表示指定了注销登录请求地址,默认是GET请求,路径为.logout。
  • invalidateHttpSession():表示是否使session失效,默认为true。
  • clearAuthentication():表示是否清除认真信息,默认为true。
  • logoutSuccessUrl():表示注销登录后的跳转地址。

​ 配置完成后,再次启动工程,登陆成功后,在浏览器中输入http://127.0.0.1:9096/logout就可以发起注销登录请求了。注销成功后,会自动跳转到loginTemplate.html页面。

​ 开发者也可以配置多个注销登录的请求,同时还可以指定请求的方法:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()),
                        new AntPathRequestMatcher("/logout2", HttpMethod.POST.name())
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessUrl("/router/loginTemplate")
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

​ 在logoutRequestMatcher()的配置表示注销请求路径,分别有两个:

  • 第一个是/logout1,请求方法是GET
  • 第二个是/logout2,请求方法是POST

​ 使用其中的任意一个请求都可以完成登陆注销。

​ 如果项目采用的是前后端分离架构,注销成功后就需要页面跳转了。只需要将注销成功的信息返回给前端即可,此时可以自定义返回内容:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()),
                        new AntPathRequestMatcher("/logout2", HttpMethod.POST.name())
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessHandler((request, response, auth) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("code", 200);
                    resultMap.put("msg", "logout success!");
                    resultMap.put("data", null);
                    String jsonResult = JSON.toJSONString(resultMap);
                    response.getWriter().write(jsonResult);
                    response.getWriter().close();
                })
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

​ 配置logoutSuccessHandler()logoutSuccessUrl()类似于之前的successHandlerdefaultSuccessUrl之间的关系,只是类不同。

​ 配置完成之后,重启项目,登陆成功后再注销登录。无论是/logout1还是/logout2进行注销,只要注销成功,就会返回JSON

​ 如果开发者希望为不同的注销地址返回不同的结果,如下配置即可:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests()
                .antMatchers("/loginFail")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/router/loginTemplate")
                .loginProcessingUrl("/loginSystem")
                .successHandler(customizeSuccessHandler())
                .failureHandler(customizeFailureHandler())
                .usernameParameter("username")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()),
                        new AntPathRequestMatcher("/logout2", HttpMethod.POST.name())
                ))
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .defaultLogoutSuccessHandlerFor((request, response, auth) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("code", 200);
                    resultMap.put("msg", "logout1 success!");
                    resultMap.put("data", null);
                    String jsonResult = JSON.toJSONString(resultMap);
                    response.getWriter().write(jsonResult);
                    response.getWriter().close();
                }, new AntPathRequestMatcher("/logout1", HttpMethod.GET.name()))
                .defaultLogoutSuccessHandlerFor((request, response, auth) -> {
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    Map<String, Object> resultMap = new HashMap<>(4);
                    resultMap.put("code", 200);
                    resultMap.put("msg", "logout2 success!");
                    resultMap.put("data", null);
                    String jsonResult = JSON.toJSONString(resultMap);
                    response.getWriter().write(jsonResult);
                    response.getWriter().close();
                }, new AntPathRequestMatcher("/logout2", HttpMethod.POST.name()))
                .permitAll()
                .and()
                .csrf().disable().build();
    }

    protected CustomizeAuthenticationSuccessHandler customizeSuccessHandler() {
        return new CustomizeAuthenticationSuccessHandler();
    }

    protected CustomizeAuthenticationFailureHandler customizeFailureHandler() {
        return new CustomizeAuthenticationFailureHandler();
    }

}

​ 通过defaultLogoutSuccessHandlerFor()方法可以注册多个不同的注销成功回调函数,该方法第一个参数是注销成功回调,第二个参数则是具体的注销登录请求。当用户注销成功之后,请求哪个注销地址,就会返回对应的响应信息。

到此这篇关于Spring Security登录表单配置的文章就介绍到这了,更多相关Spring Security登录表单内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringSecurity 自定义表单登录的实现

    本篇主要讲解 在SpringSecurity中 如何 自定义表单登录 , SpringSecurity默认提供了一个表单登录,但是实际项目里肯定无法使用的,本篇就主要讲解如何自定义表单登录 1.创建SpringSecurity项目 1.1 使用IDEA 先通过IDEA 创建一个SpringBoot项目 并且依赖SpringSecurity,Web依赖 此时pom.xml会自动添加 <dependency> <groupId>org.springframework.boot</

  • SpringSecurity 默认表单登录页展示流程源码

    SpringSecurity 默认表单登录页展示流程源码 本篇主要讲解 SpringSecurity提供的默认表单登录页 它是如何展示的的流程, 涉及 1.FilterSecurityInterceptor, 2.ExceptionTranslationFilc,xmccmc,ter , 3.DefaultLoginPageGeneratingFilter 过滤器, 并且简单介绍了 AccessDecisionManager 投票机制  1.准备工作(体验SpringSecurity默认表单认证

  • SpringBoot基于SpringSecurity表单登录和权限验证的示例

    一.简介 上篇介绍了一个自己做的管理系统,最近空闲的时间自己在继续做,把之前登录时候自定义的拦截器过滤器换成了基于SpringSecurity来做,其中遇到了很多坑,总结下,大家有遇到类似问题的话就当是为大家闭坑吧. 二.项目实现功能和成果展示 首先来看下登录界面:这是我输入的一个正确的信息,点击登录后SpringSecurity会根据你输入的用户名和密码去验证是否正确,如果正确的话就去你定义的页面,我这里定义的是查询教师信息页面.来看下代码吧. 三.准备工作(前台页面.实体类) 实体类Teac

  • SpringSecurity 表单登录的实现

    目录 表单登录 登录成功 登录失败 注销登录 自定义注销成功的返回内容 表单登录 @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated()

  • Spring Security在标准登录表单中添加一个额外的字段

    概述 在本文中,我们将通过向标准登录表单添加额外字段来实现Spring Security的自定义身份验证方案. 我们将重点关注两种不同的方法,以展示框架的多功能性以及我们可以使用它的灵活方式. 我们的第一种方法是一个简单的解决方案,专注于重用现有的核心Spring Security实现. 我们的第二种方法是更加定制的解决方案,可能更适合高级用例. 2. Maven设置 我们将使用Spring Boot启动程序来引导我们的项目并引入所有必需的依赖项.  我们将使用的设置需要父声明,Web启动器和安

  • Spring Security 表单登录功能的实现方法

    1.简介 本文将重点介绍使用 Spring Security 登录. 本文将构建在之前简单的 Spring MVC示例 之上,因为这是设置Web应用程序和登录机制的必不可少的. 2. Maven 依赖 要将Maven依赖项添加到项目中,请参阅Spring Security with Maven 一文. 标准的 spring-security-web 和 spring-security-config 都是必需的. 3. Spring Security Java配置 我们首先创建一个扩展 WebSe

  • Spring Security登录表单配置示例详解

    目录 Spring Security登录表单配置 1.引入pom依赖 2.bootstrap.yml添加配置 3.创建login.html 4.创建配置类 5.配置细节 6.登陆成功 7.登陆失败 8.注销登录 Spring Security登录表单配置 1.引入pom依赖 ​ 创建一个Spring Boot工程,引入Web和Spring Security依赖: <?xml version="1.0" encoding="UTF-8"?> <pro

  • BootStrap智能表单实战系列(八)表单配置json详解

    本章属于该系列的高级部分,将介绍表单中一些列的配置 1.config列的配置: 主要用于控制布局 :config:{autoLayout:true|'1,2,2,4'} true:根据配置项最里层的数量来自动使用不同的栅格, '1,2,2,4':使用指定的栅格来布局,如果配置的列数不足的情况将使用第一项(n,n 为一项) 2.hides的配置项 hides:[{id:'xxx',value:''}] 此项是可选的,主要用于编辑时存放一些不可见的列(如主键ID的值) 3.eles 表单元素的配置(

  • JavaScript表单验证示例详解

    HTML表单(form)通常用于收集用户信息,例如姓名,电子邮件地址,位置,年龄等. 但是很可能某些用户可能不会输入您期望的数据.HTML表单验证可以通过JavaScript完成. 为了避免对服务器资源造成不必要的压力,您可以使用JavaScript在客户端(用户系统)上验证表单数据,不正确的信息是不会提交给后台服务器的--这是所谓客户端验证.本文将介绍这种验证 表单验证一般分为两种方式. 客户端验证:直接在客户端执行JS进行验证,验证的过程中和服务器端没有任何的交互 服务器端验证:页面将验证信

  • BootStrap智能表单demo示例详解

    1.基本配置,支持的元素类型 2.自动布局 3.自定义布局 4.自定义表单 5.数据绑定 6.带验证的表单 7.智能搜索 8.级联下拉 9.图片上传 图片有点大了,屏幕不够大的话可能看的不习惯,没事 后面我截图的实际尽量弄小点O(∩_∩)O~~ 接下来进入实战吧:感兴趣的朋友请持续关注下篇.

  • Spring Boot 2 Thymeleaf服务器端表单验证实现详解

    这篇文章主要介绍了Spring Boot 2 Thymeleaf服务器端表单验证实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 表单验证分为前端验证和服务器端验证. 服务器端验证方面,Java提供了主要用于数据验证的JSR 303规范,而Hibernate Validator实现了JSR 303规范. 项目依赖加入spring-boot-starter-thymeleaf时,默认就会加入Hibernate Validator的依赖. 开

  • React实现登录表单的示例代码

    作为一个Vue用户,是时候扩展一下React了,从引入antd.配置less.router,终于实现了一个简单的登录表单. 代码如下: import React from 'react'; import { Input, Button, message } from "antd"; import { UserOutlined, LockOutlined, EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons'; impor

  • Spring Security 多过滤链的使用详解

    目录 一.背景 二.需求 1.给客户端使用的api 2.给网站使用的api 三.实现方案 方案一: 方案二 四.实现 1.app 端 Spring Security 的配置 五.实现效果 1.app 有权限访问 api 2.app 无权限访问 api 3.admin 用户有权限访问 网站 api 4.dev 用户无权限访问 网站 api 六.完整代码 一.背景 在我们实际的开发过程中,有些时候可能存在这么一些情况,某些api 比如: /api/** 这些是给App端使用的,数据的返回都是以JSO

  • Spring Security动态权限的实现方法详解

    目录 1. 动态管理权限规则 1.1 数据库设计 1.2 实战 2. 测试 最近在做 TienChin 项目,用的是 RuoYi-Vue 脚手架,在这个脚手架中,访问某个接口需要什么权限,这个是在代码中硬编码的,具体怎么实现的,松哥下篇文章来和大家分析,有的小伙伴可能希望能让这个东西像 vhr 一样,可以在数据库中动态配置,因此这篇文章和小伙伴们简单介绍下 Spring Security 中的动态权限方案,以便于小伙伴们更好的理解 TienChin 项目中的权限方案. 1. 动态管理权限规则 通

  • SpringMVC实现表单验证功能详解

    本章节内容很丰富,主要有基本的表单操作,数据的格式化,数据的校验,以及提示信息的国际化等实用技能. 首先看效果图 项目结构图 接下来用代码重点学习SpringMVC的表单操作,数据格式化,数据校验以及错误提示信息国际化.请读者将重点放在UserController.java,User.java,input.jsp三个文件中. maven 项目必不可少的pom.xml文件.里面有该功能需要的所有jar包. <?xml version="1.0" encoding="UTF

  • PHP实现动态表单生成工具详解

    目录 Form介绍 特点 项目主页链接 安装方法 快速使用 链式操作创建块表单 数组配置创建块表单 行内表单 table表单 表单包含多种input类型,包括 hiiden类型 ,text类型,radio类型,checkbox类型,textarea类型,file类型,select类型等基础类型,手写表单就是累耗时耗力开发销量太低而且代码量大了还容易写出bug,每个页面的表单遇到改动的时候恨不得长十双手去改,于是我自己开发了一个php写的表单生成工具,在业务逻辑通过配置或者链式操作去初始表单结构和

随机推荐