JavaWeb-SpringSecurity記住我功能

 

 

  系列博文html

  項目已上傳至guthub  傳送門java

  JavaWeb-SpringSecurity初認識  傳送門mysql

  JavaWeb-SpringSecurity在數據庫中查詢登錄用戶  傳送門git

  JavaWeb-SpringSecurity自定義登錄頁面  傳送門github

  JavaWeb-SpringSecurity實現需求-判斷請求是否以html結尾  傳送門web

  JavaWeb-SpringSecurity自定義登錄配置  傳送門spring

  JavaWeb-SpringSecurity圖片驗證ImageCode  傳送門sql

  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
相關文章
相關標籤/搜索