zoukankan      html  css  js  c++  java
  • JavaWeb-SpringSecurity记住我功能

      系列博文

      项目已上传至guthub  传送门

      JavaWeb-SpringSecurity初认识  传送门

      JavaWeb-SpringSecurity在数据库中查询登陆用户  传送门

      JavaWeb-SpringSecurity自定义登陆页面  传送门

      JavaWeb-SpringSecurity实现需求-判断请求是否以html结尾  传送门

      JavaWeb-SpringSecurity自定义登陆配置  传送门

      JavaWeb-SpringSecurity图片验证ImageCode  传送门

      JavaWeb-SpringSecurity记住我功能  传送门

      JavaWeb-SpringSecurity使用短信验证码登陆  传送门

      在login.html中添加一个复选框,表示"记住我"功能【注意:<input>标签的name一定是remember-me】

        <form action="/loginPage" method="post">
        
            用户名:
            <input type="text" name="username">
            <br>
            
            密码:
            <input type="password" name="password">
            <br>
            
            图片验证码:
            <input type="text" name="imageCode">
            <img src="/code/image">
            <br>
            
            <input name="remember-me" type="checkbox" value="true">
            记住我
            
            <input type="submit">
        
        </form>

      

      在config层SecurityConfig.java中添加persistentTokenRepository()方法,用来在server层操作数据库

        @Autowired    
        private DataSource dataSource;
        
        //负责操作数据库
        public PersistentTokenRepository  persistentTokenRepository()
        {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }

      JdbcTokenRepositoryImpl要操作数据库,得在数据库中存在操作存储用户信息token数据库表,使用JdbcTokenRepositoryImpl接口中提供创建数据库语句

    /** Default SQL for creating the database table to store the tokens */
        public static final String CREATE_TABLE_SQL = "create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, "
                + "token varchar(64) not null, last_used timestamp not null)";
        /** The default SQL used by the <tt>getTokenBySeries</tt> query */
        public static final String DEF_TOKEN_BY_SERIES_SQL = "select username,series,token,last_used from persistent_logins where series = ?";
        /** The default SQL used by <tt>createNewToken</tt> */
        public static final String DEF_INSERT_TOKEN_SQL = "insert into persistent_logins (username, series, token, last_used) values(?,?,?,?)";
        /** The default SQL used by <tt>updateToken</tt> */
        public static final String DEF_UPDATE_TOKEN_SQL = "update persistent_logins set token = ?, last_used = ? where series = ?";
        /** The default SQL used by <tt>removeUserTokens</tt> */
        public static final String DEF_REMOVE_USER_TOKENS_SQL = "delete from persistent_logins where username = ?";

      

    create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)
    gary.sql

      在SecurityConfig.java实现"记住我"功能

    @Autowired    
        private DataSource dataSource;
        
        //负责操作数据库
        public PersistentTokenRepository  persistentTokenRepository()
        {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }
        
        @Autowired    
        public UserDetailsService userDetailService;
        
        
        protected void configure(HttpSecurity http) throws Exception{
            
            //声明我们自己写的过滤器
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            //给过滤器赋值
            validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
            validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
            validateCodeFilter.afterPropertiesSet();
            
            //表单验证(身份认证)
            http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                //自定义登陆页面
                .loginPage("/require")
                //如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
                .loginProcessingUrl("/loginPage")
                //配置登陆成功调用loginSuccessHandler
                .successHandler(loginSuccessHandler)
                //配置登陆失败调用loginFailureHandler
                .failureHandler(loginFailureHandler)
                //记住我功能
                .and()
                .rememberMe()
                //配置persistentTokenRepository
                .tokenRepository(persistentTokenRepository())
                //配置userDetailsService
                .userDetailsService(userDetailService)
                .and()
                //请求授权
                .authorizeRequests()
                //在访问我们的URL时,我们是不需要省份认证,可以立即访问
                .antMatchers("/login.html","/require","/code/image").permitAll()
                //所有请求都被拦截,跳转到(/login请求中)
                .anyRequest()
                //都需要我们身份认证
                .authenticated()
                //SpringSecurity保护机制
                .and().csrf().disable();
        }
    package com.Gary.GaryRESTful.config;
    
    import javax.sql.DataSource;
    
    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.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
    import com.Gary.GaryRESTful.handler.LoginFailureHandler;
    import com.Gary.GaryRESTful.handler.LoginSuccessHandler;
    import com.Gary.GaryRESTful.properties.GarySecurityProperties;
    
    
    //Web应用安全适配器
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
        //告诉SpringSecurity密码用什么加密的
        @Bean
        public PasswordEncoder passwordEncoder()
        {
            return new BCryptPasswordEncoder();
        }
        
        @Autowired
        private LoginSuccessHandler loginSuccessHandler;
    
        @Autowired
        private LoginFailureHandler loginFailureHandler;
         
        @Autowired
        private GarySecurityProperties garySecurityProperties;
        
        @Autowired    
        private DataSource dataSource;
        
        //负责操作数据库
        public PersistentTokenRepository  persistentTokenRepository()
        {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }
        
        @Autowired    
        public UserDetailsService userDetailService;
        
        
        protected void configure(HttpSecurity http) throws Exception{
            
            //声明我们自己写的过滤器
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            //给过滤器赋值
            validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
            validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
            validateCodeFilter.afterPropertiesSet();
            
            //表单验证(身份认证)
            http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                //自定义登陆页面
                .loginPage("/require")
                //如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
                .loginProcessingUrl("/loginPage")
                //配置登陆成功调用loginSuccessHandler
                .successHandler(loginSuccessHandler)
                //配置登陆失败调用loginFailureHandler
                .failureHandler(loginFailureHandler)
                //记住我功能
                .and()
                .rememberMe()
                //配置persistentTokenRepository
                .tokenRepository(persistentTokenRepository())
                //配置userDetailsService
                .userDetailsService(userDetailService)
                .and()
                //请求授权
                .authorizeRequests()
                //在访问我们的URL时,我们是不需要省份认证,可以立即访问
                .antMatchers("/login.html","/require","/code/image").permitAll()
                //所有请求都被拦截,跳转到(/login请求中)
                .anyRequest()
                //都需要我们身份认证
                .authenticated()
                //SpringSecurity保护机制
                .and().csrf().disable();
        }
        
    }
    SecurityConfig.java

      为防止一直记住用户,在GaryRESTful.properties中的GarySecurityProperties()方法下,配置token过期时间

        //LoginType登陆的方式,默认为JSON(restful设计风格)
        private LoginType loginType = LoginType.JSON;
    
        private ValidateCodeProperties code = new ValidateCodeProperties();
        
        private int rememberMeSeconds = 60*60;
    
        //getter()、setter()

      在application.properties中配置Token过期时间

    #Token过期时间
    gary.security.rememberMeSeconds = 3600

      在SecurityConfig.java下的configure()方法中配置过期秒数

        protected void configure(HttpSecurity http) throws Exception{
            
            //声明我们自己写的过滤器
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            //给过滤器赋值
            validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
            validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
            validateCodeFilter.afterPropertiesSet();
            
            //表单验证(身份认证)
            http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                //自定义登陆页面
                .loginPage("/require")
                //如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
                .loginProcessingUrl("/loginPage")
                //配置登陆成功调用loginSuccessHandler
                .successHandler(loginSuccessHandler)
                //配置登陆失败调用loginFailureHandler
                .failureHandler(loginFailureHandler)
                //记住我功能
                .and()
                .rememberMe()
                //配置persistentTokenRepository
                .tokenRepository(persistentTokenRepository())
                //配置过期秒数
                .tokenValiditySeconds(garySecurityProperties.getRememberMeSeconds())
                //配置userDetailsService
                .userDetailsService(userDetailService)
                .and()
                //请求授权
                .authorizeRequests()
                //在访问我们的URL时,我们是不需要省份认证,可以立即访问
                .antMatchers("/login.html","/require","/code/image").permitAll()
                //所有请求都被拦截,跳转到(/login请求中)
                .anyRequest()
                //都需要我们身份认证
                .authenticated()
                //SpringSecurity保护机制
                .and().csrf().disable();
        }
    package com.Gary.GaryRESTful.config;
    
    import javax.sql.DataSource;
    
    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.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
    import com.Gary.GaryRESTful.handler.LoginFailureHandler;
    import com.Gary.GaryRESTful.handler.LoginSuccessHandler;
    import com.Gary.GaryRESTful.properties.GarySecurityProperties;
    
    
    //Web应用安全适配器
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
        //告诉SpringSecurity密码用什么加密的
        @Bean
        public PasswordEncoder passwordEncoder()
        {
            return new BCryptPasswordEncoder();
        }
        
        @Autowired
        private LoginSuccessHandler loginSuccessHandler;
    
        @Autowired
        private LoginFailureHandler loginFailureHandler;
         
        @Autowired
        private GarySecurityProperties garySecurityProperties;
        
        @Autowired    
        private DataSource dataSource;
        
        //负责操作数据库
        public PersistentTokenRepository  persistentTokenRepository()
        {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }
        
        @Autowired    
        public UserDetailsService userDetailService;
        
        
        protected void configure(HttpSecurity http) throws Exception{
            
            //声明我们自己写的过滤器
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            //给过滤器赋值
            validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
            validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
            validateCodeFilter.afterPropertiesSet();
            
            //表单验证(身份认证)
            http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                //自定义登陆页面
                .loginPage("/require")
                //如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
                .loginProcessingUrl("/loginPage")
                //配置登陆成功调用loginSuccessHandler
                .successHandler(loginSuccessHandler)
                //配置登陆失败调用loginFailureHandler
                .failureHandler(loginFailureHandler)
                //记住我功能
                .and()
                .rememberMe()
                //配置persistentTokenRepository
                .tokenRepository(persistentTokenRepository())
                //配置过期秒数
                .tokenValiditySeconds(garySecurityProperties.getRememberMeSeconds())
                //配置userDetailsService
                .userDetailsService(userDetailService)
                .and()
                //请求授权
                .authorizeRequests()
                //在访问我们的URL时,我们是不需要省份认证,可以立即访问
                .antMatchers("/login.html","/require","/code/image").permitAll()
                //所有请求都被拦截,跳转到(/login请求中)
                .anyRequest()
                //都需要我们身份认证
                .authenticated()
                //SpringSecurity保护机制
                .and().csrf().disable();
        }
        
    }
    SecurityConfig.java

      测试:每次用户勾选了了记住我,在persistent_logins表中就会多处一条token记录【如果用户不勾选记住我,persistent_logins表中不会多处token记录】

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    <h1>Gary登陆页面</h1>
        <form action="/loginPage" method="post">
        
            用户名:
            <input type="text" name="username">
            <br>
            
            密码:
            <input type="password" name="password">
            <br>
            
            图片验证码:
            <input type="text" name="imageCode">
            <img src="/code/image">
            <br>
            
            <input name="remember-me" type="checkbox" value="true">
            记住我
            
            <input type="submit">
        
        </form>
    
    </body>
    </html>
    login.html
    #datasource
    spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.dricer-class-name=com.mysql.jdbc.Driver
    
    #jpa
    #打印出数据库语句
    spring.jpa.show-sql=true
    #更新数据库表
    spring.jpa.hibernate.ddl-auto=update
    
    #配置登陆方式
    gary.security.loginType = JSON
    
    server.port=8081
    
    #验证码长度
    gary.security.code.image.length = 6
    #验证码图片的长
    gary.security.code.image.width = 100
    
    #配置哪些需要我们验证码的Filter
    gary.security.code.image.url = /user,/user/*
    
    #Token过期时间
    gary.security.rememberMeSeconds = 3600
    application.properties
    package com.Gary.GaryRESTful.properties;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix = "gary.security")
    public class GarySecurityProperties {
        
        //LoginType登陆的方式,默认为JSON(restful设计风格)
        private LoginType loginType = LoginType.JSON;
    
        private ValidateCodeProperties code = new ValidateCodeProperties();
        
        private int rememberMeSeconds = 60*60;
        
        public int getRememberMeSeconds() {
            return rememberMeSeconds;
        }
    
        public void setRememberMeSeconds(int rememberMeSeconds) {
            this.rememberMeSeconds = rememberMeSeconds;
        }
    
        public ValidateCodeProperties getCode() {
            return code;
        }
    
        public void setCode(ValidateCodeProperties code) {
            this.code = code;
        }
    
        public LoginType getLoginType() {
            return loginType;
        }
    
        public void setLoginType(LoginType loginType) {
            this.loginType = loginType;
        }
        
        
        
    }
    GarySecurityProperties.java
    package com.Gary.GaryRESTful.config;
    
    import javax.sql.DataSource;
    
    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.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
    import com.Gary.GaryRESTful.handler.LoginFailureHandler;
    import com.Gary.GaryRESTful.handler.LoginSuccessHandler;
    import com.Gary.GaryRESTful.properties.GarySecurityProperties;
    
    
    //Web应用安全适配器
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
        //告诉SpringSecurity密码用什么加密的
        @Bean
        public PasswordEncoder passwordEncoder()
        {
            return new BCryptPasswordEncoder();
        }
        
        @Autowired
        private LoginSuccessHandler loginSuccessHandler;
    
        @Autowired
        private LoginFailureHandler loginFailureHandler;
         
        @Autowired
        private GarySecurityProperties garySecurityProperties;
        
        @Autowired    
        private DataSource dataSource;
        
        //负责操作数据库
        public PersistentTokenRepository  persistentTokenRepository()
        {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
        }
        
        @Autowired    
        public UserDetailsService userDetailService;
        
        
        protected void configure(HttpSecurity http) throws Exception{
            
            //声明我们自己写的过滤器
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            //给过滤器赋值
            validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
            validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
            validateCodeFilter.afterPropertiesSet();
            
            //表单验证(身份认证)
            http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                //自定义登陆页面
                .loginPage("/require")
                //如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
                .loginProcessingUrl("/loginPage")
                //配置登陆成功调用loginSuccessHandler
                .successHandler(loginSuccessHandler)
                //配置登陆失败调用loginFailureHandler
                .failureHandler(loginFailureHandler)
                //记住我功能
                .and()
                .rememberMe()
                //配置persistentTokenRepository
                .tokenRepository(persistentTokenRepository())
                //配置过期秒数
                .tokenValiditySeconds(garySecurityProperties.getRememberMeSeconds())
                //配置userDetailsService
                .userDetailsService(userDetailService)
                .and()
                //请求授权
                .authorizeRequests()
                //在访问我们的URL时,我们是不需要省份认证,可以立即访问
                .antMatchers("/login.html","/require","/code/image").permitAll()
                //所有请求都被拦截,跳转到(/login请求中)
                .anyRequest()
                //都需要我们身份认证
                .authenticated()
                //SpringSecurity保护机制
                .and().csrf().disable();
        }
        
    }
    SecurityConfig.java
  • 相关阅读:
    DataItem 的使用[转帖]
    xmpp协议阅读总结
    smart pointer shared_from_this的使用
    std IO库, stringstream, 简看1
    const成员函数, const member function
    enum 随笔
    分隔和截断字符串, boost string algorithm library中的split和trim
    C++中异常处理
    boost::thread中的锁
    函数对象function object 以及boost::bind的一点了解
  • 原文地址:https://www.cnblogs.com/1138720556Gary/p/11819888.html
Copyright © 2011-2022 走看看