Spring Security 實現記住我


 

開篇一張圖,道理全靠悟。

 

示例如下:

1.    新建Maven項目  remember_me

 

2.   pom.xml

<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>
    <groupId>com.java</groupId>
    <artifactId>remember_me</artifactId>
    <version>1.0.0</version>

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


    <dependencies>

        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>



        <!-- 熱部署 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
            <version>1.2.8.RELEASE</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 

3.   RememberMeStarter.java

package com.java;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * <blockquote><pre>
 * 
 * 主啟動類
 * 
 * </pre></blockquote>
 * 
 * @author Logan
 *
 */
@SpringBootApplication
public class RememberMeStarter {

    public static void main(String[] args) {
        SpringApplication.run(RememberMeStarter.class, args);
    }

}

 

4.   ApplicationContextConfig.java

package com.java.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * 配置文件類
 * 
 * @author Logan
 *
 */
@Configuration
public class ApplicationContextConfig {

    /**
     * <blockquote><pre>
     * 
     * 配置密碼編碼器,Spring Security 5.X必須配置,否則登錄時報空指針異常
     * 
     * </pre></blockquote>
     * 
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

 

5.   RepositoryConfig.java

package com.java.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

/**
 * 數據庫相關配置
 * 
 * @author Logan
 *
 */
@Configuration
public class RepositoryConfig {

    @Bean
    public PersistentTokenRepository tokenRepository(DataSource dataSource) {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // tokenRepository.setCreateTableOnStartup(true); // 第一次啟動時可使用此功能自動創建表,第二次要關閉,否則表已存在會啟動報錯
        return tokenRepository;
    }

}

 

6.   SecurityUserDetailsService.java

package com.java.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * UserDetailsService實現類
 * 
 * @author Logan
 *
 */
@Component
public class SecurityUserDetailsService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 數據庫存儲密碼為加密后的密文(明文為123456)
        String password = passwordEncoder.encode("123456");

        System.out.println("username: " + username);
        System.out.println("password: " + password);

        // 模擬查詢數據庫,獲取屬於Admin和Normal角色的用戶
        User user = new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("Admin,Normal"));

        return user;
    }

}

 

7.   LoginConfig.java

package com.java.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

/**
 * 登錄相關配置
 * 
 * @author Logan
 *
 */
@Configuration
public class LoginConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private PersistentTokenRepository tokenRepository;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()

                // 設置不需要授權的請求
                .antMatchers("/js/*", "/login.html").permitAll()

                // 其它任何請求都需要驗證權限
                .anyRequest().authenticated()

                // 設置自定義表單登錄頁面
                .and().formLogin().loginPage("/login.html")

                // 設置登錄驗證請求地址為自定義登錄頁配置action ("/login/form")
                .loginProcessingUrl("/login/form")

                // 設置默認登錄成功跳轉頁面
                .defaultSuccessUrl("/main.html")

                // 添加記住我功能
                .and().rememberMe().tokenRepository(tokenRepository)

                // 有效期為兩周
                .tokenValiditySeconds(3600 * 24 * 14)

                // 設置UserDetailsService
                .userDetailsService(userDetailsService)

                // 暫時停用csrf,否則會影響驗證
                .and().csrf().disable();
    }

}

 

8.  src/main/resources  下文件如下

 

9.   application.properties

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.32.10:3306/security?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

 

10.   login.html

<!DOCTYPE html>
<html>

    <head>
        <title>登錄</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    </head>

    <body>

        <!--登錄框-->
        <div align="center">
            <h2>用戶自定義登錄頁面</h2>
            <fieldset style="width: 300px;">
                <legend>登錄框</legend>
                <form action="/login/form" method="post">
                    <table>
                        <tr>
                            <th>用戶名:</th>
                            <td><input name="username" /> </td>
                        </tr>
                        <tr>
                            <th>密碼:</th>
                            <td><input type="password" name="password" /> </td>
                        </tr>
                        <tr>
                            <th>記住我:</th>
                            <td><input type="checkbox" name="remember-me" value="true" checked="checked" /></td>
                        </tr>
                        <tr>
                            <th></th>
                            <td></td>
                        </tr>
                        <tr>
                            <td colspan="2" align="center"><button type="submit">登錄</button></td>
                        </tr>
                    </table>
                </form>
            </fieldset>

        </div>

    </body>

</html>

 

11.   main.html

<!DOCTYPE html>
<html>

    <head>
        <title>首頁</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
        <script>
            function getHostMessage() {
                $.ajax({
                    type: "get",
                    url: "/getHostMessage",
                    async: true,
                    success: function(data) {
                        $("#msg").val(JSON.stringify(data));
                    }
                });
            }
        </script>
    </head>

    <body>

        <div>
            <h2>首頁</h2>
            <table>
                <tr>
                    <td><button onclick="getHostMessage()">獲取主機信息</button></td>
                </tr>
            </table>

        </div>

        <!--響應內容-->
        <div>
            <textarea id="msg" style="width: 800px;height: 800px;"></textarea>
        </div>

    </body>

</html>

 

12.   js/jquery-3.3.1.min.js 可在官網下載

https://code.jquery.com/jquery-3.3.1.min.js

 

13.   創建數據庫

DROP DATABASE IF EXISTS security;
CREATE DATABASE security;
USE security;
create table persistent_logins (
    username varchar(64) not null, 
    series varchar(64) primary key, 
    token varchar(64) not null, 
    last_used timestamp not null
);

 

14.   運行 RememberMeStarter.java , 啟動測試

瀏覽器輸入首頁  http://localhost:8080/main.html

地址欄自動跳轉到登錄頁面,如下:

 

輸入如下信息:

 

User:Logan

Password:123456

 

 單擊【登錄】按鈕,自動跳轉到首頁。

觀察數據庫,此時自動生成一條記錄,username字段值為登錄時使用用戶名,記住我Token信息已生成。

如下所示:

 

 

測試【記住我】 功能是否生效

關閉瀏覽器重新打開,或者關閉系統重新啟動,再次訪問首頁,頁面不再跳轉到登錄頁,直接顯示首頁信息。

如下所示:

 

 

 

 

 

 

 

 

 

 

 

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM