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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值