六、记住密码

作者: 紫荆秋雪_文 | 来源:发表于2020-05-04 23:57 被阅读0次

    源码下载

    一、记住密码原理 记住密码.png

    记住密码原理.png

    二、代码配置

    • 登录界面
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
    
    <form action="/authentication/form" method="POST">
        <span>用户名称</span><input type="text" name="username"/> <br>
        <span>用户密码</span><input type="password" name="password"/> <br>
        <span>图形验证码:</span><input type="text" name="imageCode"/> <img src="/code/image?raven_width=200&raven_height=40"> <br>
        <input type="checkbox" name="remember-me" value="true"/>记住我 <br>
        <input type="submit" value="登陆">
    </form>
    
    </body>
    </html>
    
    • BrowserSecurityConfig
    package com.raven.browser;
    
    import com.raven.browser.authentication.BrowserAuthenticationFailureHandler;
    import com.raven.browser.authentication.BrowserAuthenticationSuccessHandler;
    import com.raven.core.properties.RavenSecurityProperties;
    import com.raven.core.validate.filter.RavenValidateCodeFilter;
    import com.zaxxer.hikari.HikariDataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    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.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    
    /**
     * Web端security配置
     */
    @Configuration
    public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private RavenSecurityProperties securityProperties;
        @Autowired
        private BrowserAuthenticationSuccessHandler browserAuthenticationSuccessHandler;
        @Autowired
        private BrowserAuthenticationFailureHandler browserAuthenticationFailureHandler;
        @Autowired
        private RavenValidateCodeFilter validateCodeFilter;
        @Autowired
        private HikariDataSource hikariDataSource;
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Bean
        public PersistentTokenRepository persistentTokenRepository() {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(this.hikariDataSource);
    //        tokenRepository.setCreateTableOnStartup(true);
            return tokenRepository;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 配置登录界面
            String loginPage = this.securityProperties.getBrowser().getLoginPage();
            int tokenTime = this.securityProperties.getBrowser().getTokenTime();
    
            http.csrf().disable()
                    .addFilterBefore(this.validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                    .formLogin()
                    .loginPage("/authentication/require")    // 当需要身份认证时,跳转到这里
                    .loginProcessingUrl("/authentication/form") // 默认处理的/login,自定义登录界面需要指定请求路径
                    .successHandler(this.browserAuthenticationSuccessHandler)
                    .failureHandler(this.browserAuthenticationFailureHandler)
                    .and()
                    .rememberMe()
                    .tokenRepository(persistentTokenRepository())
                    .tokenValiditySeconds(tokenTime)
                    .userDetailsService(this.userDetailsService)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/authentication/require",
                            loginPage,
                            "/code/image"
                    ).permitAll()
                    .anyRequest()
                    .authenticated();
        }
    }
    

    三、源码

    • AbstractAuthenticationProcessingFilter
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
                throws IOException, ServletException {
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            if (!requiresAuthentication(request, response)) {
                chain.doFilter(request, response);
    
                return;
            }
    
            if (logger.isDebugEnabled()) {
                logger.debug("Request is to process authentication");
            }
    
            Authentication authResult;
    
            try {
                // 通过attemptAuthentication可以得到授权信息
                authResult = attemptAuthentication(request, response);
                if (authResult == null) {
                    // return immediately as subclass has indicated that it hasn't completed
                    // authentication
                    return;
                }
                sessionStrategy.onAuthentication(authResult, request, response);
            }
            catch (InternalAuthenticationServiceException failed) {
                logger.error(
                        "An internal error occurred while trying to authenticate the user.",
                        failed);
                unsuccessfulAuthentication(request, response, failed);
    
                return;
            }
            catch (AuthenticationException failed) {
                // Authentication failed
                unsuccessfulAuthentication(request, response, failed);
    
                return;
            }
    
            // Authentication success
            if (continueChainBeforeSuccessfulAuthentication) {
                chain.doFilter(request, response);
            }
    
            successfulAuthentication(request, response, chain, authResult);
        }
    
    • successfulAuthentication()授权成功
    protected void successfulAuthentication(HttpServletRequest request,
                HttpServletResponse response, FilterChain chain, Authentication authResult)
                throws IOException, ServletException {
    
            if (logger.isDebugEnabled()) {
                logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
                        + authResult);
            }
    
            // 把授权信息存储到sesiion中
            SecurityContextHolder.getContext().setAuthentication(authResult);
    
            rememberMeServices.loginSuccess(request, response, authResult);
    
            // Fire event
            if (this.eventPublisher != null) {
                eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
                        authResult, this.getClass()));
            }
    
            successHandler.onAuthenticationSuccess(request, response, authResult);
        }
    
    • AbstractRememberMeServices记住密码
    @Override
        public final void loginSuccess(HttpServletRequest request,
                HttpServletResponse response, Authentication successfulAuthentication) {
    
            if (!rememberMeRequested(request, parameter)) {
                logger.debug("Remember-me login not requested.");
                return;
            }
    
            onLoginSuccess(request, response, successfulAuthentication);
        }
    
    • PersistentTokenBasedRememberMeServices
    protected void onLoginSuccess(HttpServletRequest request,
                HttpServletResponse response, Authentication successfulAuthentication) {
            String username = successfulAuthentication.getName();
    
            logger.debug("Creating new persistent login for user " + username);
    
            // 创建 PersistentRememberMeToken
            PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
                    username, generateSeriesData(), generateTokenData(), new Date());
            try {
                // 通过 PersistentRememberMeToken 来创建新的token
                tokenRepository.createNewToken(persistentToken);
                addCookie(persistentToken, request, response);
            }
            catch (Exception e) {
                logger.error("Failed to save persistent token ", e);
            }
        }
    
    • createNewToken() 通过getJdbcTemplate保存或更新token信息到数据库
    public void createNewToken(PersistentRememberMeToken token) {
            getJdbcTemplate().update(insertTokenSql, token.getUsername(), token.getSeries(),
                    token.getTokenValue(), token.getDate());
        }
    
    • addCookie(persistentToken, request, response);添加token到Cookie中

    • 调用自己定义的成功处理器

    protected void successfulAuthentication(HttpServletRequest request,
                HttpServletResponse response, FilterChain chain, Authentication authResult)
                throws IOException, ServletException {
    
            if (logger.isDebugEnabled()) {
                logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
                        + authResult);
            }
    
            SecurityContextHolder.getContext().setAuthentication(authResult);
    
            rememberMeServices.loginSuccess(request, response, authResult);
    
            // Fire event
            if (this.eventPublisher != null) {
                eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
                        authResult, this.getClass()));
            }
    
            // 调用我们自己定义的成功处理器
            successHandler.onAuthenticationSuccess(request, response, authResult);
        }
    

    相关文章

      网友评论

        本文标题:六、记住密码

        本文链接:https://www.haomeiwen.com/subject/ynfughtx.html