系列博文
项目已上传至guthub 传送门
JavaWeb-SpringSecurity初认识 传送门
JavaWeb-SpringSecurity在数据库中查询登陆用户 传送门
JavaWeb-SpringSecurity自定义登陆页面 传送门
JavaWeb-SpringSecurity实现需求-判断请求是否以html结尾 传送门
JavaWeb-SpringSecurity自定义登陆配置 传送门
JavaWeb-SpringSecurity图片验证ImageCode 传送门
JavaWeb-SpringSecurity记住我功能 传送门
JavaWeb-SpringSecurity使用短信验证码登陆 传送门
在login.html中添加一个复选框,表示"记住我"功能【注意:标签的name一定是remember-me】
用户名:
密码:
图片验证码:
记住我
在config层SecurityConfig.java中添加persistentTokenRepository()方法,用来在server层操作数据库
@AutowiredprivateDataSource dataSource;//负责操作数据库
publicPersistentTokenRepository persistentTokenRepository()
{
JdbcTokenRepositoryImpl tokenRepository= newJdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);returntokenRepository;
}
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 getTokenBySeries 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 createNewToken*/
public static final String DEF_INSERT_TOKEN_SQL = "insert into persistent_logins (username, series, token, last_used) values(?,?,?,?)";/**The default SQL used by updateToken*/
public static final String DEF_UPDATE_TOKEN_SQL = "update persistent_logins set token = ?, last_used = ? where series = ?";/**The default SQL used by removeUserTokens*/
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实现"记住我"功能
@AutowiredprivateDataSource dataSource;//负责操作数据库
publicPersistentTokenRepository persistentTokenRepository()
{
JdbcTokenRepositoryImpl tokenRepository= newJdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);returntokenRepository;
}
@AutowiredpublicUserDetailsService userDetailService;protected void configure(HttpSecurity http) throwsException{//声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = newValidateCodeFilter();//给过滤器赋值
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();
}
packagecom.Gary.GaryRESTful.config;importjavax.sql.DataSource;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;importorg.springframework.security.web.authentication.rememberme.PersistentTokenRepository;importcom.Gary.GaryRESTful.filter.ValidateCodeFilter;importcom.Gary.GaryRESTful.handler.LoginFailureHandler;importcom.Gary.GaryRESTful.handler.LoginSuccessHandler;importcom.Gary.GaryRESTful.properties.GarySecurityProperties;//Web应用安全适配器
@Configurationpublic class SecurityConfig extendsWebSecurityConfigurerAdapter{//告诉SpringSecurity密码用什么加密的
@BeanpublicPasswordEncoder passwordEncoder()
{return newBCryptPasswordEncoder();
}
@AutowiredprivateLoginSuccessHandler loginSuccessHandler;
@AutowiredprivateLoginFailureHandler loginFailureHandler;
@AutowiredprivateGarySecurityProperties garySecurityProperties;
@AutowiredprivateDataSource dataSource;//负责操作数据库
publicPersistentTokenRepository persistentTokenRepository()
{
JdbcTokenRepositoryImpl tokenRepository= newJdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);returntokenRepository;
}
@AutowiredpublicUserDetailsService userDetailService;protected void configure(HttpSecurity http) throwsException{//声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = newValidateCodeFilter();//给过滤器赋值
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 = newValidateCodeProperties();private int rememberMeSeconds = 60*60;//getter()、setter()
在application.properties中配置Token过期时间
#Token过期时间
gary.security.rememberMeSeconds = 3600
在SecurityConfig.java下的configure()方法中配置过期秒数
protected void configure(HttpSecurity http) throwsException{//声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = newValidateCodeFilter();//给过滤器赋值
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();
}
packagecom.Gary.GaryRESTful.config;importjavax.sql.DataSource;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;importorg.springframework.security.web.authentication.rememberme.PersistentTokenRepository;importcom.Gary.GaryRESTful.filter.ValidateCodeFilter;importcom.Gary.GaryRESTful.handler.LoginFailureHandler;importcom.Gary.GaryRESTful.handler.LoginSuccessHandler;importcom.Gary.GaryRESTful.properties.GarySecurityProperties;//Web应用安全适配器
@Configurationpublic class SecurityConfig extendsWebSecurityConfigurerAdapter{//告诉SpringSecurity密码用什么加密的
@BeanpublicPasswordEncoder passwordEncoder()
{return newBCryptPasswordEncoder();
}
@AutowiredprivateLoginSuccessHandler loginSuccessHandler;
@AutowiredprivateLoginFailureHandler loginFailureHandler;
@AutowiredprivateGarySecurityProperties garySecurityProperties;
@AutowiredprivateDataSource dataSource;//负责操作数据库
publicPersistentTokenRepository persistentTokenRepository()
{
JdbcTokenRepositoryImpl tokenRepository= newJdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);returntokenRepository;
}
@AutowiredpublicUserDetailsService userDetailService;protected void configure(HttpSecurity http) throwsException{//声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = newValidateCodeFilter();//给过滤器赋值
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记录】
Insert title hereGary登陆页面
用户名:
密码:
图片验证码:
记住我
login.html
#datasource
spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8spring.datasource.username=root
spring.datasource.password=123456spring.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
packagecom.Gary.GaryRESTful.properties;importorg.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix= "gary.security")public classGarySecurityProperties {//LoginType登陆的方式,默认为JSON(restful设计风格)
private LoginType loginType =LoginType.JSON;private ValidateCodeProperties code = newValidateCodeProperties();private int rememberMeSeconds = 60*60;public intgetRememberMeSeconds() {returnrememberMeSeconds;
}public void setRememberMeSeconds(intrememberMeSeconds) {this.rememberMeSeconds =rememberMeSeconds;
}publicValidateCodeProperties getCode() {returncode;
}public voidsetCode(ValidateCodeProperties code) {this.code =code;
}publicLoginType getLoginType() {returnloginType;
}public voidsetLoginType(LoginType loginType) {this.loginType =loginType;
}
}
GarySecurityProperties.java
packagecom.Gary.GaryRESTful.config;importjavax.sql.DataSource;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;importorg.springframework.security.web.authentication.rememberme.PersistentTokenRepository;importcom.Gary.GaryRESTful.filter.ValidateCodeFilter;importcom.Gary.GaryRESTful.handler.LoginFailureHandler;importcom.Gary.GaryRESTful.handler.LoginSuccessHandler;importcom.Gary.GaryRESTful.properties.GarySecurityProperties;//Web应用安全适配器
@Configurationpublic class SecurityConfig extendsWebSecurityConfigurerAdapter{//告诉SpringSecurity密码用什么加密的
@BeanpublicPasswordEncoder passwordEncoder()
{return newBCryptPasswordEncoder();
}
@AutowiredprivateLoginSuccessHandler loginSuccessHandler;
@AutowiredprivateLoginFailureHandler loginFailureHandler;
@AutowiredprivateGarySecurityProperties garySecurityProperties;
@AutowiredprivateDataSource dataSource;//负责操作数据库
publicPersistentTokenRepository persistentTokenRepository()
{
JdbcTokenRepositoryImpl tokenRepository= newJdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);returntokenRepository;
}
@AutowiredpublicUserDetailsService userDetailService;protected void configure(HttpSecurity http) throwsException{//声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = newValidateCodeFilter();//给过滤器赋值
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