六、SpringSecurity实现“记住我”功能

一、记住我功能基本原理

原理图


15200008-241bb13e480c16ea.png
记住我功能基本原理

一个请求,先进入UsernamePasswordAuthenticationFilter,当这个过滤器认证成功之后,会调用一个RemeberMeService服务,在RemeberMeService类里面有一个TokenRepository方法。
RemeberMeService这个服务会干什么呢?它会生成一个token,然后将这个token存入到浏览器的Cookie中去。同时TokenRepository方法还可以将这个Token写入到数据库中,因为我这个动作是在通过UsernamePasswordAuthenticationFilter认证成功之后去做的,所以在存入DB的时候会将用户名和token存入进去即token和用户名是一一对应的。
等第二天这个同一个用户再次访问系统的时候,这个请求在经过过滤器链的时候会经过RememberMeAuthenticationFilter过滤器,这个过滤器的作用就是读取cookie中的token,然后交给RemeberMeService,RemeberMeService会用TokenRepository到数据库中去查询这个token在数据库中有没有记录,如果有记录会将username取出来,取出来之后会调用UserDetailsService去获取用户信息,然后将用户信息存入到SecurityContext中去,以此来实现记住我功能。

RemeberMeService的过滤器链位置

15200008-45a8f2fa7c904fb5.png
112.png

二、记住我功能具体实现

1.页面处理
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <h1>标准登录页面</h1>
    <h3>表单登录</h3>
        <form action="/authentication/form" method="post">
            <table>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="username"/></td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td><input type="password" name="password"/></td>
                </tr>
                <tr>
                    <td>验证码:</td>
                    <td>
                        <input type="text" name="imageCode"/>
                        <img src="/code/image?width=150">
                    </td>
                </tr>
                <tr>
                    <td colspan="2"><input name="remember-me" type="checkbox" value="true"/>记住我</td>
                </tr>
                <tr>
                    <td colspan="2"><button type="submit">登录</button></td>
                </tr>
            </table>
        </form>

</body>
</html>
2.配置数据库

因为添加"记住我"这个功能需要用到DB,所以我在properties文件中去加入我的数据库的信息

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tinner-demo?useUnicode=yes
spring.datasource.username=root
spring.datasource.password=root
3.配置BrowserSecurityConfig

首先去配置PersistentTokenRepository ,在这个里面将dataSource注入进去,然后在这个类中有一个tokenRepository.setCreateTableOnStartup(true);方法,这个方法会去db中新建一个存储token和用户名的表,然后项目其中之后,会自动地去DB中新建这个表,但是在之后就不能再把这个打开了,需要注释掉。


    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
//        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

然后在BrowserProperties中去设置一个默认的记住我的时间,这个也是可以在配置文件中去配置的。默认我写了3600秒

package com.tinner.security.core.properties;

public class BrowserProperties {

    private String loginPage = "/tinner-signIn.html";

    private LoginType loginType = LoginType.JSON;

    private int rememberMeSecond = 3600;

    public int getRememberMeSecond() {
        return rememberMeSecond;
    }

    public void setRememberMeSecond(int rememberMeSecond) {
        this.rememberMeSecond = rememberMeSecond;
    }

    public LoginType getLoginType() {
        return loginType;
    }

    public void setLoginType(LoginType loginType) {
        this.loginType = loginType;
    }

    public String getLoginPage() {
        return loginPage;
    }

    public void setLoginPage(String loginPage) {
        this.loginPage = loginPage;
    }
}

然后在configure方法中配置

 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                    .loginPage("/authentication/require")
                    .loginProcessingUrl("/authentication/form")
                    .successHandler(tinnerAuthentivationSuccessHandler)
                    .failureHandler(tinnerAuthentivationFailureHandler)
                .and()
                    .rememberMe()
                    .tokenRepository(persistentTokenRepository())
                    .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSecond())
                    .userDetailsService(userDetailsService)

就是要去调用rememberMe服务,然后调用tokenRepository方法, 设置一个cookie有效时间,最后返回回去的时候调用userDetailsService这个服务,将用户信息返回给前台。

三、记住我功能SpringSecurity源码解析

1.登录解析

首先,请求会进入UsernamePasswordAuthenticationFilter中,校验完用户名密码之后会调用rememberMeService的loginSuccess方法


15200008-7c9214120aa6fd58.png
记住我功能SpringSecurity源码解析

在这个登录成功方法里面,它做了两件事,第一件事是用tokenRepository去创建一个新的token存入到数据库中。第二件是就是将生成的token存入到浏览器的cookie中去。


15200008-045f7b016957112f.png
登录成功的方法
2.在有remeberMe功能开启的情况下再次登录解析

现在浏览器中输入url,然后请求进入到了RememberMeAuthenticationFilter过滤器中,它首先会判断SecurityContextHolder中是不是有一个认证过的Authentication,如果没有就会去调用rememberMeService的autoLogin方法


15200008-203de324a7ceeb48.png
在有remeberMe功能开启的情况下再次登录解析

在这个方法中首先会从请求的cookie中拿到token,然后再调用getTokenForService去数据库中去拿相应的token和用户名信息。如果token没值就会抛出异常,如果有值则会去进行判断(token是否过期等)


15200008-a29f4cf98c01b7ea.png
144.png

15200008-77e91bf87550f2b7.png
1222

这些检查都通过了,最终会调用getUserDetailsService的loadUserByUsername方法。
15200008-40db6ebe45bf0e65.png
585.png

即用找到的那个用户名去调用UserDetailService去获取用户信息,最终返回到RememberMeAuthenticationFilter。

然后RememberMeAuthenticationFilter拿到了之后会将用户信息存入到session中去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值