SpringBoot - 安全入门与SpringSecurity

市面上有两种常见的安全框架,Shiro和SpringSecurity。功能都很强大,用户数量也都很多。SpringSecurity优势在于能和Spring无缝衔接。

Shiro系列教程:https://blog.csdn.net/j080624/article/category/7006814

Shiro官网地址:https://shiro.apache.org/

Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型。他可以实现强大的web安全控制。对于安全控制,我们仅需引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理。


【1】认证和授权

应用程序的两个主要区域是“认证”和“授权”(或者访问控制)。这两个主要区域是Spring Security 的两个目标。

“认证”(Authentication),是建立一个他声明的主体的过程(一个“主体”一般是指用户,设备或一些可以在你的应用程序中执行动作的其他系统)。

“授权”(Authorization)指确定一个主体是否允许在你的应用程序执行一个动作的过程。为了抵达需要授权的店,主体的身份已经有认证过程建立。

这个概念是通用的而不只在Spring Security中。


【2】Spring Security

使用注解@EnableWebSecurity开启WebSecurity模式。

该注解源码如下:

/**
 * Add this annotation to an {@code @Configuration} class to have the Spring Security
 * configuration defined in any {@link WebSecurityConfigurer} or more likely by extending
 * the {@link WebSecurityConfigurerAdapter} base class and overriding individual methods:
 *在配置类上添加该注解使其获得Spring Security配置,该配置被定义在任何
 WebSecurityConfigurer中或继承自WebSecurityConfigurerAdapter并重写方法的
 类中,后者如下所示:
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableWebSecurity
 * public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
 *
 *  &#064;Override
 *  public void configure(WebSecurity web) throws Exception {
 *      web.ignoring()
 *      // Spring Security should completely ignore URLs starting with /resources/
 *              .antMatchers(&quot;/resources/**&quot;);
 *  }
 *
 *  &#064;Override
 *  protected void configure(HttpSecurity http) throws Exception {
 *      http.authorizeRequests().antMatchers(&quot;/public/**&quot;).permitAll().anyRequest()
 *              .hasRole(&quot;USER&quot;).and()
 *              // Possibly more configuration ...
 *              .formLogin() // enable form based log in
 *              // set permitAll for all URLs associated with Form Login
 *              .permitAll();
 *  }
 *
 *  &#064;Override
 *  protected void configure(AuthenticationManagerBuilder auth) {
 *      auth
 *      // enable in memory based authentication with a user named &quot;user&quot; and &quot;admin&quot;
 *      .inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;)
 *              .and().withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
 *  }
 *
 *  // Possibly more overridden methods ...
 * }
 * </pre>
 *
 * @see WebSecurityConfigurer
 * @see WebSecurityConfigurerAdapter
 *
 * @author Rob Winch
 * @since 3.2
 */
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
        SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication//开启全局权限配置
@Configuration// 这里引用了@Configuration该注解
public @interface EnableWebSecurity {

    /**
     * Controls debugging support for Spring Security. Default is false.
     * @return if true, enables debug support with Spring Security
     */
    boolean debug() default false;
}

两个重要的类:

WebSecurityConfigurerAdapter:自定义Security策略

AuthenticationManagerBuilder:自定义认证策略


【3】源码与测试

① 基础环境

默认欢迎页面如下(未登录状态),不同权限可以查看不同武功秘籍:

这里写图片描述


② 引入SpringSecurity

Starter如下:

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

自定义Security配置类初始如下:

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    // 如下,定制请求的授权规则
        http.authorizeRequests()
            .antMatchers("/css/**", "/").permitAll()
            .antMatchers("/level1/**").hasRole("VIP1")
            .antMatchers("/level2/**").hasRole("VIP2")
            .antMatchers("/level3/**").hasRole("VIP3")
    }
}

此时再访问任意武林秘籍,则会提示访问被拒绝如下图:

这里写图片描述


③ 开启自动配置的登录功能

http.formLogin();方法会自动创建默认登录页。默认/login请求就会来到登录页,如果登录失败则会重定向到/login?error。也可以自定义登录页,查看HttpSecurity.formLogin()源码示例如下:

/**
     * Specifies to support form based authentication.
     * //指定支持基于表单的身份验证。 
     * If
     * {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page
     * will be generated.
     *// 如果登录页没有指定,则会创建一个默认登录页
     * <h2>Example Configurations</h2>
     *
     * The most basic configuration defaults to automatically generating a login page at
     * the URL "/login", redirecting to "/login?error" for authentication failure. The
     * details of the login page can be found on
     * {@link FormLoginConfigurer#loginPage(String)}
     *
     * <pre>
     * &#064;Configuration
     * &#064;EnableWebSecurity
     * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     *
     *  &#064;Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin();
     *  }
     *
     *  &#064;Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;);
     *  }
     * }
     * </pre>
     *
     * The configuration below demonstrates customizing the defaults.
     *
     * <pre>
     * &#064;Configuration
     * &#064;EnableWebSecurity
     * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     *// 着重观察如下配置示例 !!!
     *  &#064;Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin()
     *              .usernameParameter(&quot;username&quot;) // default is username
     *              .passwordParameter(&quot;password&quot;) // default is password
     *              .loginPage(&quot;/authentication/login&quot;) // default is /login with an HTTP get
     *              .failureUrl(&quot;/authentication/login?failed&quot;) // default is /login?error
     *              .loginProcessingUrl(&quot;/authentication/login/process&quot;); // default is /login
     *                                                                      // with an HTTP
     *                                                                      // post
     *  }
     *
     *  &#064;Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;);
     *  }
     * }
     * </pre>
     *
     * @see FormLoginConfigurer#loginPage(String)
     *
     * @return
     * @throws Exception
     */
    public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
        return getOrApply(new FormLoginConfigurer<HttpSecurity>());
    }

此时还配置任何用户,没有登录没有权限,访问Level1,会跳向默认的登录页:

这里写图片描述


④ 为系统添加用户、密码和角色

配置类中添加方法:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("zhangsan").password("123456").roles("VIP1","VIP2")
                .and()
                .withUser("lisi").password("123456").roles("VIP3","VIP2")
                .and()
                .withUser("wangwu").password("123456").roles("VIP3","VIP1");

    }

该种方法是将用户保存在内存中,项目中应该使用数据库,如下:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username,password, enabled from users where username = ?")
                .authoritiesByUsernameQuery("select username, role from user_roles where username = ?");
    }

此时不同用户就拥有了不同权限,如zhangsan只能访问普通和高级武功秘籍,访问绝世武功秘籍就会提示访问被拒绝:

这里写图片描述


⑤ 开启自动注销功能http.formLogout()

看源码注释:

/**
     * Provides logout support. This is automatically applied when using
     * {@link WebSecurityConfigurerAdapter}.
     * // 提供了注销支持。这种能力是自动被应用的当使用了WebSecurityConfigurerAdapter。
     *  The default is that accessing the URL
     * "/logout" will log the user out by invalidating the HTTP Session, cleaning up any
     * {@link #rememberMe()} authentication that was configured, clearing the
     * {@link SecurityContextHolder}, and then redirect to "/login?success".
     *// 默认应用于/logout请求,删除会话session,清除rememberMe权限配置并重定向到/login?success请求。
     * <h2>Example Custom Configuration</h2>
     *
     * The following customization to log out when the URL "/custom-logout" is invoked.
     * Log out will remove the cookie named "remove", not invalidate the HttpSession,
     * clear the SecurityContextHolder, and upon completion redirect to "/logout-success".
     *
     * <pre>
     * &#064;Configuration
     * &#064;EnableWebSecurity
     * public class LogoutSecurityConfig extends WebSecurityConfigurerAdapter {
     *
     *  &#064;Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin()
     *              .and()
     *              // sample logout customization
     *              .logout().deleteCookies(&quot;remove&quot;).invalidateHttpSession(false)
     *              .logoutUrl(&quot;/custom-logout&quot;).logoutSuccessUrl(&quot;/logout-success&quot;);
     *  }
     *
     *  &#064;Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;);
     *  }
     * }
     * </pre>
     *
     * @return
     * @throws Exception
     */
    public LogoutConfigurer<HttpSecurity> logout() throws Exception {
        return getOrApply(new LogoutConfigurer<HttpSecurity>());
    }

修改请求访问规则配置方法如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        http.authorizeRequests()
            .antMatchers("/css/**", "/").permitAll()
            .antMatchers("/level1/**").hasRole("VIP1")
            .antMatchers("/level2/**").hasRole("VIP2")
            .antMatchers("/level3/**").hasRole("VIP3");
        // 开启自动配置的登录功能
        http.formLogin();

        // 开启自动配置的注销功能
        http.logout();
    }

在欢迎页面添加注销表单:

这里写图片描述

点击注销效果如下(重定向到了/login?logout):

这里写图片描述


修改默认退出重定向页面:

// 开启自动配置的注销功能,默认重定向到/logout?success,修改为"/"
http.logout().logoutSuccessUrl("/");

此时退出返回到主页面!


【4】Thymeleaf提供的SpringSecurity标签支持

需要引入thymeleaf-extras-springsecurity4,修改默认引入的Thymeleaf版本:

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.3.0</thymeleaf-layout-dialect.version>
        <thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity4.version>
    </properties>

dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity</artifactId>
</dependency>

页面引入security的名称空间:

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

① 测试一,修改默认访问页面提示

修改默认访问页面如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 align="center">欢迎光临武林秘籍管理系统</h1>
<!--如果没有认证,则显示如下-->
<div sec:authorize="!isAuthenticated()">
    <h2 align="center">游客您好,如果想查看武林秘籍 <a th:href="@{/login}">请登录</a></h2>
</div>
<!--如果认证了,则显示如下-->
<div sec:authorize="isAuthenticated()">
    <h2><span sec:authentication="name"></span>,您好,您的角色有:<span sec:authentication="principal.authorities"></span></h2>
    <form th:action="@{/logout}" method="post">
        <input type="submit" value="注销"/>
    </form>
</div>

没有认证时访问首页如下:

这里写图片描述


登录成功后,首页显示如下:

这里写图片描述


② 测试二,不同权限显示不同武林秘籍

修改页面如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 align="center">欢迎光临武林秘籍管理系统</h1>
<!--如果没有认证,则显示如下-->
<div sec:authorize="!isAuthenticated()">
    <h2 align="center">游客您好,如果想查看武林秘籍 <a th:href="@{/login}">请登录</a></h2>
</div>
<!--如果认证了,则显示如下-->
<div sec:authorize="isAuthenticated()">
    <h2><span sec:authentication="name"></span>,您好,您的角色有:<span sec:authentication="principal.authorities"></span></h2>
    <form th:action="@{/logout}" method="post">
        <input type="submit" value="注销"/>
    </form>
</div>

<hr>

<div sec:authorize="hasRole('VIP1')">
<h3>普通武功秘籍</h3>
<ul>
    <li><a th:href="@{/level1/1}">罗汉拳</a></li>
    <li><a th:href="@{/level1/2}">武当长拳</a></li>
    <li><a th:href="@{/level1/3}">全真剑法</a></li>
</ul>
</div>
<div sec:authorize="hasRole('VIP2')">
<h3>高级武功秘籍</h3>
<ul>
    <li><a th:href="@{/level2/1}">太极拳</a></li>
    <li><a th:href="@{/level2/2}">七伤拳</a></li>
    <li><a th:href="@{/level2/3}">梯云纵</a></li>
</ul>
</div>
<div sec:authorize="hasRole('VIP3')">
<h3>绝世武功秘籍</h3>
<ul>
    <li><a th:href="@{/level3/1}">葵花宝典</a></li>
    <li><a th:href="@{/level3/2}">龟派气功</a></li>
    <li><a th:href="@{/level3/3}">独孤九剑</a></li>
</ul>
</div>
</body>
</html>

没有登录时,显示效果如下:

这里写图片描述

登录后显示效果如下:

这里写图片描述

只显示拥有的权限目录!


【5】常见功能-Remeber me

配置启用remember-me功能

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        http.authorizeRequests()
            .antMatchers("/css/**", "/").permitAll()
            .antMatchers("/level1/**").hasRole("VIP1")
            .antMatchers("/level2/**").hasRole("VIP2")
            .antMatchers("/level3/**").hasRole("VIP3");
        // 开启自动配置的登录功能
        http.formLogin();

        // 开启自动配置的注销功能,默认重定向到/logout?success,修改为"/"
        http.logout().logoutSuccessUrl("/");

        //开启自动配置的Remember me
        http.rememberMe();
    }

此时登录页显示如下:

这里写图片描述


点击登录,查看network :

这里写图片描述
这里写图片描述


此时关闭浏览器,再次打开,直接进入登录后的页面:

这里写图片描述

点击注销,将会从浏览器删除该Cookie!
这里写图片描述


【6】自定义登录页与Remember-me

① 自定义登录页面

修改配置如下:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        http.authorizeRequests()
            .antMatchers("/css/**", "/").permitAll()
            .antMatchers("/level1/**").hasRole("VIP1")
            .antMatchers("/level2/**").hasRole("VIP2")
            .antMatchers("/level3/**").hasRole("VIP3");
        // 开启自动配置的登录功能
        http.formLogin().loginPage("/userlogin");

        // 开启自动配置的注销功能,默认重定向到/logout?success,修改为"/"
        http.logout().logoutSuccessUrl("/");

        //开启自动配置的Remember me
        http.rememberMe();
    }

修改自定义登录页面如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1 align="center">欢迎登陆武林秘籍管理系统</h1>
    <hr>
    <div align="center">
        <form th:action="@{/userlogin}" method="post">
            用户名:<input name="username"/><br>
            密&nbsp;&nbsp;&nbsp;码:<input name="password"><br/>
            <input type="submit" value="登陆">
        </form>
    </div>
</body>
</html>

访问受保护的页面或者点击登录时,会来到我们自定义的登录页面:

这里写图片描述


FormLoginConfigurer.loginPage方法源码与注释如下:

/**
     * <p>
     * Specifies the URL to send users to if login is required. 
     * 发送给用户的登录请求
     * If used with
     * {@link WebSecurityConfigurerAdapter} a default login page will be generated when
     * this attribute is not specified.
     * 如果没有被指定则使用默认的登录页面
     * </p>
     *
     * <p>
     * If a URL is specified or this is not being used in conjuction with
     * {@link WebSecurityConfigurerAdapter}, users are required to process the specified
     * URL to generate a login page. 
     * 如果url被指定或没有使用默认的登录页面,用户需要处理登录请求跳转到登录页面
     * In general, the login page should create a form that
     * submits a request with the following requirements to work with
     * {@link UsernamePasswordAuthenticationFilter}:
     * // 一般来说,登录页面需要创建一个表单提交如下所需的参数属性:
     * </p>
     *
     * <ul>
     * <li>It must be an HTTP POST</li>
     * <li>It must be submitted to {@link #loginProcessingUrl(String)}</li>
     * <li>It should include the username as an HTTP parameter by the name of
     * {@link #usernameParameter(String)}</li>
     * <li>It should include the password as an HTTP parameter by the name of
     * {@link #passwordParameter(String)}</li>
     * </ul>
     *
     * <h2>Example login.jsp</h2>
     *
     * Login pages can be rendered with any technology you choose so long as the rules
     * above are followed. Below is an example login.jsp that can be used as a quick start
     * when using JSP's or as a baseline to translate into another view technology.
     *下面是一个JSP例子,可以被用来快速创建登录页。
     * <pre>
     * <!-- loginProcessingUrl should correspond to FormLoginConfigurer#loginProcessingUrl. Don't forget to perform a POST -->
     * &lt;c:url value="/login" var="loginProcessingUrl"/&gt;
     * &lt;form action="${loginProcessingUrl}" method="post"&gt;
     *    &lt;fieldset&gt;
     *        &lt;legend&gt;Please Login&lt;/legend&gt;
     *        &lt;!-- use param.error assuming FormLoginConfigurer#failureUrl contains the query parameter error --&gt;
     *        &lt;c:if test="${param.error != null}"&gt;
     *            &lt;div&gt;
     *                Failed to login.
     *                &lt;c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}"&gt;
     *                  Reason: &lt;c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" /&gt;
     *                &lt;/c:if&gt;
     *            &lt;/div&gt;
     *        &lt;/c:if&gt;
     *        &lt;!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout --&gt;
     *        &lt;c:if test="${param.logout != null}"&gt;
     *            &lt;div&gt;
     *                You have been logged out.
     *            &lt;/div&gt;
     *        &lt;/c:if&gt;
     *        &lt;p&gt;
     *        &lt;label for="username"&gt;Username&lt;/label&gt;
     *        &lt;input type="text" id="username" name="username"/&gt;
     *        &lt;/p&gt;
     *        &lt;p&gt;
     *        &lt;label for="password"&gt;Password&lt;/label&gt;
     *        &lt;input type="password" id="password" name="password"/&gt;
     *        &lt;/p&gt;
     *        &lt;!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter --&gt;
     *        &lt;p&gt;
     *        &lt;label for="remember-me"&gt;Remember Me?&lt;/label&gt;
     *        &lt;input type="checkbox" id="remember-me" name="remember-me"/&gt;
     *        &lt;/p&gt;
     *        &lt;div&gt;
     *            &lt;button type="submit" class="btn"&gt;Log in&lt;/button&gt;
     *        &lt;/div&gt;
     *    &lt;/fieldset&gt;
     * &lt;/form&gt;
     * </pre>
     *
     * <h2>Impact on other defaults</h2>
     *
     * Updating this value, also impacts a number of other default values.
     * // 更新默认值,将会影响其他默认值
     * For example,
     * the following are the default values when only formLogin() was specified.
     *// 下面是当formLogin被开启时一系列默认值
     * <ul>
     * <li>/login GET - the login form</li>
     * <li>/login POST - process the credentials and if valid authenticate the user</li>
     * <li>/login?error GET - redirect here for failed authentication attempts</li>
     * <li>/login?logout GET - redirect here after successfully logging out</li>
     * </ul>
     *
     * If "/authenticate" was passed to this method it update the defaults as shown below:
     *
     * <ul>
     * <li>/authenticate GET - the login form</li>
     * <li>/authenticate POST - process the credentials and if valid authenticate the user
     * </li>
     * <li>/authenticate?error GET - redirect here for failed authentication attempts</li>
     * <li>/authenticate?logout GET - redirect here after successfully logging out</li>
     * </ul>
     *
     *
     * @param loginPage the login page to redirect to if authentication is required (i.e.
     * "/login")
     * @return the {@link FormLoginConfigurer} for additional customization
     */
    @Override
    public FormLoginConfigurer<H> loginPage(String loginPage) {
        return super.loginPage(loginPage);
    }

通过阅读上述源码,我们尝试修改用户名、密码默认参数名如下:

  • 修改配置类
@Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        http.authorizeRequests()
            .antMatchers("/css/**", "/").permitAll()
            .antMatchers("/level1/**").hasRole("VIP1")
            .antMatchers("/level2/**").hasRole("VIP2")
            .antMatchers("/level3/**").hasRole("VIP3");
        // 开启自动配置的登录功能
        http.formLogin().usernameParameter("name").passwordParameter("pwd").loginPage("/userlogin");

        // 开启自动配置的注销功能,默认重定向到/logout?success,修改为"/"
        http.logout().logoutSuccessUrl("/");

        //开启自动配置的Remember me
        http.rememberMe();
    }
  • 修改表单
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1 align="center">欢迎登陆武林秘籍管理系统</h1>
    <hr>
    <div align="center">
        <form th:action="@{/userlogin}" method="post">
            用户名:<input name="name"/><br>
            密&nbsp;&nbsp;&nbsp;码:<input name="pwd"><br/>
            <input type="submit" value="登陆">
        </form>
    </div>
</body>
</html>

再次测试登录:

这里写图片描述
成功登录!


② 为自定义登录页面添加Remember-me

首先查看使用默认登录页面时remember-me的源码:

这里写图片描述

可以看到,添加了一个复选框,name为”remember-me”。

同样,在我们的登录表单中,也添加该复选框即可!

修改表单如下:

<form th:action="@{/userlogin}" method="post">
    用户名:<input name="name"/><br>
    密&nbsp;&nbsp;&nbsp;码:<input name="pwd"><br/>
    <input type="submit" value="登陆">
    <input type="checkbox" name="remember-me"/> Remember-me
</form>

登录测试如下图:

这里写图片描述
这里写图片描述


同样可以查看RememberMeConfigurer源码获取记住我的默认name:

public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<RememberMeConfigurer<H>, H> {
    /**
     * The default name for remember me parameter name and remember me cookie name
     */
    private static final String DEFAULT_REMEMBER_ME_NAME = "remember-me";
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    private String key;
    private RememberMeServices rememberMeServices;
    private LogoutHandler logoutHandler;
    private String rememberMeParameter = DEFAULT_REMEMBER_ME_NAME;
    private String rememberMeCookieName = DEFAULT_REMEMBER_ME_NAME;
    private String rememberMeCookieDomain;
    private PersistentTokenRepository tokenRepository;
    private UserDetailsService userDetailsService;
    private Integer tokenValiditySeconds;
    private Boolean useSecureCookie;
    private Boolean alwaysRemember;
    //...
}

也可以使用自定义记住我name:

  • 配置方法修改
  //开启自动配置的Remember me,并修改记住我的参数name
  http.rememberMe().rememberMeParameter("remember");
  • 表单修改
<form th:action="@{/userlogin}" method="post">
    用户名:<input name="name"/><br>
    密&nbsp;&nbsp;&nbsp;码:<input name="pwd"><br/>
    <input type="checkbox" name="remember"/> Remember-me<br/>
    <input type="submit" value="登陆">
</form>

【7】CSRF

CSRF(Cross-site request forgery)跨站请求伪造。

HttpSecurity默认启用csrf功能,会为表单添加_csrf的值,提交携带来预防CSRF。

如点击登录,查看network:

这里写图片描述

本篇博文项目地址:https://github.com/JanusJ/SpringBoot/tree/master/security

猜你喜欢

转载自blog.csdn.net/j080624/article/details/80966608