源码下载
一、记住密码原理 记住密码.png
记住密码原理.png二、代码配置
- 登录界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<form action="/authentication/form" method="POST">
<span>用户名称</span><input type="text" name="username"/> <br>
<span>用户密码</span><input type="password" name="password"/> <br>
<span>图形验证码:</span><input type="text" name="imageCode"/> <img src="/code/image?raven_width=200&raven_height=40"> <br>
<input type="checkbox" name="remember-me" value="true"/>记住我 <br>
<input type="submit" value="登陆">
</form>
</body>
</html>
- BrowserSecurityConfig
package com.raven.browser;
import com.raven.browser.authentication.BrowserAuthenticationFailureHandler;
import com.raven.browser.authentication.BrowserAuthenticationSuccessHandler;
import com.raven.core.properties.RavenSecurityProperties;
import com.raven.core.validate.filter.RavenValidateCodeFilter;
import com.zaxxer.hikari.HikariDataSource;
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.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
/**
* Web端security配置
*/
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RavenSecurityProperties securityProperties;
@Autowired
private BrowserAuthenticationSuccessHandler browserAuthenticationSuccessHandler;
@Autowired
private BrowserAuthenticationFailureHandler browserAuthenticationFailureHandler;
@Autowired
private RavenValidateCodeFilter validateCodeFilter;
@Autowired
private HikariDataSource hikariDataSource;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(this.hikariDataSource);
// tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 配置登录界面
String loginPage = this.securityProperties.getBrowser().getLoginPage();
int tokenTime = this.securityProperties.getBrowser().getTokenTime();
http.csrf().disable()
.addFilterBefore(this.validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin()
.loginPage("/authentication/require") // 当需要身份认证时,跳转到这里
.loginProcessingUrl("/authentication/form") // 默认处理的/login,自定义登录界面需要指定请求路径
.successHandler(this.browserAuthenticationSuccessHandler)
.failureHandler(this.browserAuthenticationFailureHandler)
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(tokenTime)
.userDetailsService(this.userDetailsService)
.and()
.authorizeRequests()
.antMatchers("/authentication/require",
loginPage,
"/code/image"
).permitAll()
.anyRequest()
.authenticated();
}
}
三、源码
- AbstractAuthenticationProcessingFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
// 通过attemptAuthentication可以得到授权信息
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
- successfulAuthentication()授权成功
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
// 把授权信息存储到sesiion中
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
- AbstractRememberMeServices记住密码
@Override
public final void loginSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication successfulAuthentication) {
if (!rememberMeRequested(request, parameter)) {
logger.debug("Remember-me login not requested.");
return;
}
onLoginSuccess(request, response, successfulAuthentication);
}
- PersistentTokenBasedRememberMeServices
protected void onLoginSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication successfulAuthentication) {
String username = successfulAuthentication.getName();
logger.debug("Creating new persistent login for user " + username);
// 创建 PersistentRememberMeToken
PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
username, generateSeriesData(), generateTokenData(), new Date());
try {
// 通过 PersistentRememberMeToken 来创建新的token
tokenRepository.createNewToken(persistentToken);
addCookie(persistentToken, request, response);
}
catch (Exception e) {
logger.error("Failed to save persistent token ", e);
}
}
- createNewToken() 通过getJdbcTemplate保存或更新token信息到数据库
public void createNewToken(PersistentRememberMeToken token) {
getJdbcTemplate().update(insertTokenSql, token.getUsername(), token.getSeries(),
token.getTokenValue(), token.getDate());
}
-
addCookie(persistentToken, request, response);添加token到Cookie中
-
调用自己定义的成功处理器
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
// 调用我们自己定义的成功处理器
successHandler.onAuthenticationSuccess(request, response, authResult);
}
网友评论