Spring Security 实现记住我

摘要:
开篇一张图,道理全靠悟。

开篇一张图,道理全靠悟。

Spring Security 实现记住我第1张

示例如下:

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

packagecom.java;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * <blockquote><pre>
 * 
 * 主启动类
 * 
 * </pre></blockquote>
 * 
 * @authorLogan
 *
 */
@SpringBootApplication
public classRememberMeStarter {
    public static voidmain(String[] args) {
        SpringApplication.run(RememberMeStarter.class, args);
    }
}

4. ApplicationContextConfig.java

packagecom.java.config;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
importorg.springframework.security.crypto.password.PasswordEncoder;
/**
 * 配置文件类
 * 
 * @authorLogan
 *
 */
@Configuration
public classApplicationContextConfig {
    /**
     * <blockquote><pre>
     * 
     * 配置密码编码器,Spring Security 5.X必须配置,否则登录时报空指针异常
     * 
     * </pre></blockquote>
     * 
     * @return
     */
    @Bean
    publicPasswordEncoder passwordEncoder() {
        return newBCryptPasswordEncoder();
    }
}

5. RepositoryConfig.java

packagecom.java.config;
importjavax.sql.DataSource;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
importorg.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
/**
 * 数据库相关配置
 * 
 * @authorLogan
 *
 */
@Configuration
public classRepositoryConfig {
    @Bean
    publicPersistentTokenRepository tokenRepository(DataSource dataSource) {
        JdbcTokenRepositoryImpl tokenRepository = newJdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //tokenRepository.setCreateTableOnStartup(true); //第一次启动时可使用此功能自动创建表,第二次要关闭,否则表已存在会启动报错
        returntokenRepository;
    }
}

6. SecurityUserDetailsService.java

packagecom.java.service;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.security.core.authority.AuthorityUtils;
importorg.springframework.security.core.userdetails.User;
importorg.springframework.security.core.userdetails.UserDetails;
importorg.springframework.security.core.userdetails.UserDetailsService;
importorg.springframework.security.core.userdetails.UsernameNotFoundException;
importorg.springframework.security.crypto.password.PasswordEncoder;
importorg.springframework.stereotype.Component;
/**
 * UserDetailsService实现类
 * 
 * @authorLogan
 *
 */
@Component
public class SecurityUserDetailsService implementsUserDetailsService {
    @Autowired
    privatePasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throwsUsernameNotFoundException {
        //数据库存储密码为加密后的密文(明文为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"));
        returnuser;
    }
}

7. LoginConfig.java

packagecom.java.config;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.security.config.annotation.web.builders.HttpSecurity;
importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
importorg.springframework.security.core.userdetails.UserDetailsService;
importorg.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
/**
 * 登录相关配置
 * 
 * @authorLogan
 *
 */
@Configuration
public class LoginConfig extendsWebSecurityConfigurerAdapter {
    @Autowired
    privatePersistentTokenRepository tokenRepository;
    @Autowired
    privateUserDetailsService userDetailsService;
    @Override
    protected void configure(HttpSecurity http) throwsException {
        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 下文件如下

Spring Security 实现记住我第2张

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=" 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>
            functiongetHostMessage() {
                $.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=" 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 EXISTSsecurity;
CREATE DATABASEsecurity;
USEsecurity;
create tablepersistent_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

地址栏自动跳转到登录页面,如下:

Spring Security 实现记住我第3张

输入如下信息:

User:Logan

Password:123456

单击【登录】按钮,自动跳转到首页。

观察数据库,此时自动生成一条记录,username字段值为登录时使用用户名,记住我Token信息已生成。

如下所示:

Spring Security 实现记住我第4张

测试【记住我】 功能是否生效

关闭浏览器重新打开,或者关闭系统重新启动,再次访问首页,页面不再跳转到登录页,直接显示首页信息。

如下所示:

Spring Security 实现记住我第5张

.

免责声明:文章转载自《Spring Security 实现记住我》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇CSS基础深入之细说盒子模型java基础学习--最近几天看韩顺平资料学习心得(二)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

无密码远程桌面连接方法

无密码远程桌面的连接方法如下:1.设置为允许远程桌面访问。...

Idea常用插件整合

官方网站:https://plugins.jetbrains.com/plugin/228-sql-query-plugin6.IdeaVim基于IntelliJ的Vim仿真插件。注意:如果打开WebInspector,那么CSS/JavaScript同步和元素高亮显示不起作用“pluginisdebuggingthistab”信息栏的可用性问题官方网站:h...

AirtestIDE基本功能(二)

文件菜单-相应工具栏上的前四个按钮:新建、打开、保存和另存为新。单击此按钮以选择是否使用创建脚本。air后缀或带有的脚本。py后缀。新脚本将初始化代码,以帮助您从API引入Airtest的各种接口,并自动初始化设备。你可以看到。air脚本文件实际上是一个公用文件夹,其中放置了通过IDE捕获的图像和运行日志。软件关闭时,布局信息将自动保存。(3) 选项-设置设...

input框输入金额处理的解决办法

最近,已经启动的项目在删除输入输入量时突然出现问题。各种在线搜索都没有找到你想要的。今天,我将以react框架为例进行代码贡献。我会写下需求和解决方案,希望对我的朋友有用。如果有更好的方法实现它,请给我一些建议!”在“:”下;n=数学。防抱死制动系统;vars=“”;对于{s+=.replace;}S=S||“整数”;n=数学。地板对于{varp=“”;对于...

如何设置Navicat的显示字体与字体大小?

方法/步骤打开Navicat点击菜单,再选择在界面,点击下的设置网格字体和大小设置编辑器字体和大小设置命令列界面字体和大小设置ER图表字体和大小,最后点击END...

流控制、FlowControl

作用就是防止网络拥堵时导致的“丢包”问题,大致的工作原理就是当链路两端的设备有一端忙不过来了,他会给另外一端的设备发一个暂停发包的命令,通过这种方式来缓解压力,解决丢包问题。看上去流控制应该是个非常好的防止丢包的方法,但是为什么我们还要在无盘上关闭他呢?...