package com.itheima.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.itheima.service.UserDetailsServiceImpl;

import javax.sql.DataSource;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 自定义用户授权管理
        http.authorizeRequests().antMatchers("/").permitAll()
                // 需要对static文件夹下静态资源进行统一放行
                .antMatchers("/login/**").permitAll().antMatchers("/detail/common/**").hasRole("common")
                .antMatchers("/detail/vip/**").hasRole("vip").anyRequest().authenticated();

        // 自定义用户登录控制
        http.formLogin().loginPage("/userLogin").permitAll().usernameParameter("name").passwordParameter("pwd")
                .defaultSuccessUrl("/").failureUrl("/userLogin?error");

        // 自定义用户退出控制
        http.logout().logoutUrl("/mylogout").logoutSuccessUrl("/");

        // 定制Remember-me记住我功能
        http.rememberMe().rememberMeParameter("rememberme").tokenValiditySeconds(200);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 密码需要设置编码器
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        // 1、使用内存用户信息,作为测试使用
//        auth.inMemoryAuthentication().passwordEncoder(encoder).withUser("shitou").password(encoder.encode("123456"))
//                .roles("common").and().withUser("李四").password(encoder.encode("123456")).roles("vip");
//        
        // 2、使用JDBC进行身份认证
//        String userSQL = "select username,password,valid from t_customer " + "where username = ?";
//        
//        String authoritySQL = "select c.username,a.authority from t_customer c,t_authority a,"
//                + "t_customer_authority ca where ca.customer_id=c.id " + "and ca.authority_id=a.id and c.username =?";
//        
//        auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource).usersByUsernameQuery(userSQL)
//                .authoritiesByUsernameQuery(authoritySQL);

        // 3、使用UserDetailsService进行身份认证
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>用户登录界面</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
    <form class="form-signin" th:action="@{/userLogin}" th:method="post" >
        <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72px" height="72px">
        <h1 class="h3 mb-3 font-weight-normal">请登录</h1>
        <!-- 用户登录错误信息提示框 -->
        <div th:if="${param.error}" style="color: red;height: 40px;text-align: left;font-size: 1.1em">
            <img th:src="@{/login/img/loginError.jpg}" width="20px">用户名或密码错误,请重新登录!
        </div>
        <input type="text" name="name" class="form-control" placeholder="用户名" required="" autofocus="">
        <input type="password" name="pwd" class="form-control" placeholder="密码" required="">
        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" name="rememberme"> 记住我
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" >登录</button>
        <p class="mt-5 mb-3 text-muted">Copyright© 2019-2020</p>
    </form>
</body>
</html>
上面是简单的token加密方式,下面是持久化的token方式。
package com.itheima.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import com.itheima.service.UserDetailsServiceImpl;

import javax.sql.DataSource;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 自定义用户授权管理
        http.authorizeRequests().antMatchers("/").permitAll()
                // 需要对static文件夹下静态资源进行统一放行
                .antMatchers("/login/**").permitAll().antMatchers("/detail/common/**").hasRole("common")
                .antMatchers("/detail/vip/**").hasRole("vip").anyRequest().authenticated();

        // 自定义用户登录控制
        http.formLogin().loginPage("/userLogin").permitAll().usernameParameter("name").passwordParameter("pwd")
                .defaultSuccessUrl("/").failureUrl("/userLogin?error");

        // 自定义用户退出控制
        http.logout().logoutUrl("/mylogout").logoutSuccessUrl("/");

        // 定制Remember-me记住我功能
        http.rememberMe().rememberMeParameter("rememberme").tokenValiditySeconds(200)
                // 对cookie信息进行持久化管理
                .tokenRepository(tokenRepository());
    }

    /**
     * 持久化Token存储
     * 
     * @return
     */
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository() {
        JdbcTokenRepositoryImpl jr = new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        return jr;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 密码需要设置编码器
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        // 1、使用内存用户信息,作为测试使用
//        auth.inMemoryAuthentication().passwordEncoder(encoder).withUser("shitou").password(encoder.encode("123456"))
//                .roles("common").and().withUser("李四").password(encoder.encode("123456")).roles("vip");
//        
        // 2、使用JDBC进行身份认证
//        String userSQL = "select username,password,valid from t_customer " + "where username = ?";
//        
//        String authoritySQL = "select c.username,a.authority from t_customer c,t_authority a,"
//                + "t_customer_authority ca where ca.customer_id=c.id " + "and ca.authority_id=a.id and c.username =?";
//        
//        auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource).usersByUsernameQuery(userSQL)
//                .authoritiesByUsernameQuery(authoritySQL);

        // 3、使用UserDetailsService进行身份认证
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

}
# 记住我功能中创建持久化Token存储的数据表
create table persistent_logins (username varchar(64) not null,
                                series varchar(64) primary key,
                                token varchar(64) not null,
                                last_used timestamp not null);