本篇介绍如下在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