系列博文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)
在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(); } }
爲防止一直記住用戶,在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(); } }
測試:每次用戶勾選了了記住我,在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>
#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
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; } }
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(); } }