本篇介绍如下在SpringBoot下使用SpringSecurity实现记住我(就是下次自动登录功能),以MySQL为例(数据库结构都一样,其他数据库稍加修改)
首先数据库建表
CREATE TABLE `t_persistent_logins` ( `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `series` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `last_used` datetime DEFAULT NULL, PRIMARY KEY (`series`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='保持登录表';
修改SpringSecurity的配置类,增加自动登录相关
WebSecurityConfig.java 主要增加记住我(自动登录)相关的配置 如下
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity()
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String SECURITY_KEY = "my-app";
@Autowired
private MyUserDetailService myUserDetailService;
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
MyJdbcTokenRepositoryImpl jdbcTokenRepository = new MyJdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
jdbcTokenRepository.setCreateTableOnStartup(false);
return jdbcTokenRepository;
}
@Bean
public PersistentTokenBasedRememberMeServices rememberMeServices() {
return new PersistentTokenBasedRememberMeServices(SECURITY_KEY, myUserDetailService, persistentTokenRepository());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**", "/login*").permitAll() //任何人都可以访问
.anyRequest().authenticated() //其他所有资源都需要认证,登陆后访问
.and()
.formLogin().loginPage("/login")
.authenticationDetailsSource(new MyWebAuthenticationDetailsSource())
.permitAll()
// 这里主要增加记住我(自动登录)相关的配置
.and().rememberMe().key(SECURITY_KEY).tokenRepository(persistentTokenRepository()).userDetailsService(myUserDetailService)
.and().logout().logoutUrl("/logout").permitAll()
.and().csrf().disable()
;
//UserDetails实现类Account那边要 重写equals()和hashCode()
}
@Override
public void configure(WebSecurity web) {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/assets/**", "/assets_mnt/**", "/api/**", "/dashboard/**");
web.ignoring().antMatchers("/**/fav.ico", "/favicon.ico", "/robots.txt");
web.ignoring().antMatchers("/**/captcha.jpg");
web.ignoring().antMatchers("/ping", "/test", "/404_error", "/generic_error", "/error");
}
}
MyUserDetailService.java
import com.terrynow.test.entity.Account;
import com.terrynow.test.service.intf.ISystemService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.annotation.Resource;
/**
* @author Terry E-mail: yaoxinghuo at 126 dot com
* @date 2021-7-7 19:34
* @description
*/
@Configuration("myUserDetailService")
public class MyUserDetailService implements UserDetailsService {
private static final Log log = LogFactory.getLog(MyUserDetailService.class);
@Resource(name = "systemService")
private ISystemService systemService;
// 用户一登陆就会调用此方法验证
@Override
public UserDetails loadUserByUsername(String no) throws UsernameNotFoundException {
Account account = systemService.getAccountByNo(no); // 数据库中查询此用户
if (account == null) { // 判断此用户是否存在
throw new UsernameNotFoundException(no);
}
if (!account.isEnabled() || !account.isAccountNonLocked()) {
throw new LockedException("账户已停用");
}
log.warn("Cookie登录,姓名:" + account.getName() + ",帐号:" + account.getNo() + ",ID:" + account.getId());
return account;
}
}
MyJdbcTokenRepositoryImpl.java
这是操作数据库(存入读取自动登录相关信息)的关键类
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
* @author Terry E-mail: yaoxinghuo at 126 dot com
* @date 2018-3-15 08:58
* @description
*/
public class MyJdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {
public static final String TABLE_NAME = "t_persistent_logins";
public static final String CREATE_TABLE_SQL = "create table "+TABLE_NAME+" (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)";
private String tokensBySeriesSql = "select username,series,token,last_used from "+TABLE_NAME+" where series = ?";
private String insertTokenSql = "insert into "+TABLE_NAME+" (username, series, token, last_used) values(?,?,?,?)";
private String updateTokenSql = "update "+TABLE_NAME+" set token = ?, last_used = ? where series = ?";
public static String removeUserTokensSql = "delete from "+TABLE_NAME+" where username = ?";
private boolean createTableOnStartup;
protected void initDao() {
if (this.createTableOnStartup) {
this.getJdbcTemplate().execute(CREATE_TABLE_SQL);
}
}
public void createNewToken(PersistentRememberMeToken token) {
this.getJdbcTemplate().update(this.insertTokenSql, new Object[]{token.getUsername(), token.getSeries(), token.getTokenValue(), token.getDate()});
}
public void updateToken(String series, String tokenValue, Date lastUsed) {
this.getJdbcTemplate().update(this.updateTokenSql, new Object[]{tokenValue, new Date(), series});
}
public PersistentRememberMeToken getTokenForSeries(String seriesId) {
try {
return this.getJdbcTemplate().queryForObject(this.tokensBySeriesSql, new RowMapper<PersistentRememberMeToken>() {
public PersistentRememberMeToken mapRow(ResultSet rs, int rowNum) throws SQLException {
return new PersistentRememberMeToken(rs.getString(1), rs.getString(2), rs.getString(3), rs.getTimestamp(4));
}
}, new Object[]{seriesId});
} catch (EmptyResultDataAccessException var3) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Querying token for series '" + seriesId + "' returned no results.", var3);
}
} catch (IncorrectResultSizeDataAccessException var4) {
this.logger.error("Querying token for series '" + seriesId + "' returned more than one value. Series" + " should be unique");
} catch (DataAccessException var5) {
this.logger.error("Failed to load token for series " + seriesId, var5);
}
return null;
}
public void removeUserTokens(String username) {
this.getJdbcTemplate().update(this.removeUserTokensSql, new Object[]{username});
}
public void setCreateTableOnStartup(boolean createTableOnStartup) {
this.createTableOnStartup = createTableOnStartup;
}
}
登录页面
写一个简单的登录页面login.html,有关记住我,只要form里增加一个name是remember-me的checkbox就可以了
<html>
<head></head>
<body>
<h1>SpringSecurity登录</h1>
<form name='f' action="login" method='POST'>
<table>
<tr>
<td>用户名:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>密码:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td>下次自动登录:</td>
<td><input type="checkbox" name="remember-me"></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="登录" /></td>
</tr>
</table>
</form>
</body>
</html>
勾选登录成功后,查看数据库表t_persistent_logins是否增加了一条记录,如果有,就说明成功了!
文章评论
Very much the helpful information