SpringSecurity-9-實(shí)現(xiàn)通過手機(jī)短信進(jìn)行認(rèn)證功能

SpringSecurity-9-實(shí)現(xiàn)通過手機(jī)短信進(jìn)行認(rèn)證功能

手機(jī)短信流程分析

手機(jī)號(hào)登錄的時(shí)候是不需要密碼登錄的嘉冒,而是通過短信驗(yàn)證碼實(shí)現(xiàn)免密登錄。具體步驟如下 :

  1. 向手機(jī)發(fā)送驗(yàn)證碼衔憨,第三方短信發(fā)送平臺(tái),如阿里云短信
  2. 手機(jī)獲取驗(yàn)證碼后站超,在表單中輸入驗(yàn)證碼
  3. 使用自定義過濾器SmsCodeValidateFilter
  4. 短信校驗(yàn)通過后,使用自定義手機(jī)認(rèn)證過濾器SmsCodeAuthenticationFilter校驗(yàn)手機(jī)號(hào)碼是否存在
  5. 自定義SmsCodeAuthenticationToken提供給SmsCodeAuthenticationFilter
  6. 自定義SmsCodeAuthenticationProvider提供給AuthenticationManager
  7. 創(chuàng)建針對(duì)手機(jī)號(hào)查詢用戶信息的SmsCodeUserDetailsService寸癌,提交給SmsCodeAuthenticationProvider
  8. 自定義SmsCodeSecurityConfig配置類將上面組件連接起來
  9. SmsCodeSecurityConfig添加到LearnSrpingSecurity安全配置的過濾器鏈上
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

創(chuàng)建短信發(fā)送接口

  • 定義發(fā)生短信的服務(wù)接口com.security.learn.sms.SmsCodeSend

代碼如下:

public interface SmsCodeSend {
? ? boolean sendSmsCode(String mobile, String code);
}
  • 實(shí)現(xiàn)短信發(fā)生服務(wù)接口com.security.learn.sms.impl.SmsCodeSendImpl代碼如下
@Slf4j
public class SmsCodeSendImpl implements SmsCodeSend {
? ? @Override
? ? public boolean sendSmsCode(String mobile, String code) {
? ? ? ? String sendCode = String.format("你好你的驗(yàn)證碼%s,請(qǐng)勿泄露他人弱贼。", code);
? ? ? ? log.info("向手機(jī)號(hào)" + mobile + "發(fā)送的短信為:" + sendCode);
? ? ? ? return true;
? ? }
}

:因?yàn)檫@里是示例所以就沒有真正的使用第三方發(fā)送短信平臺(tái)蒸苇。

  • smsCodeSend注入到容器實(shí)現(xiàn)如下
@Configuration
public class Myconfig {
? ? @Bean
? ? public PasswordEncoder passwordEncoder(){
? ? ? ? return? new BCryptPasswordEncoder();
? ? }
? ? @Bean
? ? @ConditionalOnMissingBean(SmsCodeSend.class)
? ? public SmsCodeSend smsCodeSend(){
? ? ? ? return? new SmsCodeSendImpl();
? ? }
}

手機(jī)登錄頁與發(fā)送短信驗(yàn)證碼

  • 創(chuàng)建SmsController實(shí)現(xiàn)短信發(fā)送驗(yàn)證碼的API,代碼如下:
@Controller
public class SmsController {
? ? public static final String SESSION_KEY = "SESSION_KEY_MOBILE_CODE";
? ? @RequestMapping("/mobile/page")
? ? public String toMobilePage(){
? ? ? ? return "login-mobile";
? ? }
? ? @Autowired
? ? private SmsCodeSend smsCodeSend;
? ? /**
? ? *生成手機(jī)驗(yàn)證碼并發(fā)送
? ? * @param request
? ? * @return? ? */
? ? @RequestMapping("/code/mobile")
? ? @ResponseBody
? ? public String smsCode(HttpServletRequest request){
? ? ? ? // 1. 生成一個(gè)手機(jī)驗(yàn)證碼
? ? ? ? String code = RandomStringUtils.randomNumeric(4);
? ? ? ? request.getSession().setAttribute(SESSION_KEY, code);
? ? ? ? String mobile = request.getParameter("mobile");
? ? ? ? smsCodeSend.sendSmsCode(mobile, code);
? ? ? ? return "200";
? ? }
}
  • src\main\resources\templates文件夾下添加login-mobile.html靜態(tài)頁面,具體實(shí)現(xiàn)如下
<!--suppress ALL-->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
? ? <meta charset="utf-8">
? ? <meta http-equiv="X-UA-Compatible" content="IE=edge">
? ? <title>springboot葵花寶典手機(jī)登錄</title>
? ? <!-- Tell the browser to be responsive to screen width -->
? ? <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body >
<div >
? ? <a href="#">springboot葵花寶典手機(jī)登錄</a>? <br>
? ? <a th:href="@{/login/page}" href="login.html" >
? ? ? ? <span>使用密碼驗(yàn)證登錄</span>
? ? </a> <br>
? ? <form th:action="@{/mobile/form}" action="index.html" method="post">
? ? ? ? <span>手機(jī)號(hào)碼</span> <input id="mobile" name="mobile" type="text" class="form-control" placeholder="手機(jī)號(hào)碼"><br>
? ? ? ? <span>驗(yàn)證碼</span> <input type="text" name="smsCode" class="form-control" placeholder="驗(yàn)證碼">? <a id="sendCode" th:attr="code_url=@{/code/mobile?mobile=}" href="#"> 獲取驗(yàn)證碼 </a><br>
? ? ? ? <!-- 提示信息, 表達(dá)式紅線沒關(guān)系哮洽,忽略它 -->
? ? ? ? <div th:if="${param.error}">
? ? ? ? ? ? <span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION?.message}" style="color:#ff0000"></span>
? ? ? ? </div>

? ? ? ? <span>記住我</span><input type="checkbox" name="remember-me-test" >? <br>
? ? ? ? <button type="submit" class="btn btn-primary btn-block">登錄</button>
? ? </form>

</div>
<script th:src="@{/plugins/jquery/jquery.min.js}" src="plugins/jquery/jquery.min.js"></script>
<script>
? ? // 發(fā)送驗(yàn)證碼
? ? $("#sendCode").click(function () {
? ? ? ? var mobile = $('#mobile').val().trim();
? ? ? ? if(mobile == '') {
? ? ? ? ? ? alert("手機(jī)號(hào)不能為空");
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? var url = $(this).attr("code_url") + mobile;
? ? ? ? $.get(url, function(data){
? ? ? ? ? ? alert(data === "200" ? "發(fā)送成功": "發(fā)送失敗");
? ? ? ? });
? ? });
</script>
</body>
</html>
  • LearnSrpingSecurityconfigure(HttpSecurity http)方法中添加手機(jī)免密登錄允許的url
? .and().authorizeRequests()
? ? ? ? ? ? ? ? .antMatchers("/login/page","/code/image","/mobile/page","/code/mobile").permitAll()
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

短信驗(yàn)證碼校驗(yàn)過濾器 SmsCodeValidateFilter

短信驗(yàn)證碼的校驗(yàn)過濾器填渠,實(shí)際上和圖片驗(yàn)證過濾器原理一致。都都是繼承OncePerRequestFilter實(shí)現(xiàn)一個(gè)Spring環(huán)境下的過濾器鸟辅。@Component注解不可少其核心校驗(yàn)規(guī)則如下:

  • 登錄時(shí)候手機(jī)號(hào)碼不可為空
  • 登錄時(shí)手機(jī)輸入碼不可為空
  • 登錄時(shí)輸入的短信驗(yàn)證碼必須和“謎底”中的驗(yàn)證碼一致
@Component
public class SmsCodeValidateFilter extends OncePerRequestFilter {

? ? @Autowired
? ? MyAuthenticationFailureHandler failureHandler;

? ? @Override
? ? protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
? ? ? ? if (request.getRequestURI().equals("/mobile/form")
? ? ? ? ? ? ? ? && request.getMethod().equalsIgnoreCase("post")) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? validate(request);

? ? ? ? ? ? }catch (AuthenticationException e){
? ? ? ? ? ? ? ? failureHandler.onAuthenticationFailure(
? ? ? ? ? ? ? ? ? ? ? ? request,response,e);
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? filterChain.doFilter(request,response);
? ? }

? ? private void validate(HttpServletRequest request)? {
? ? ? ? //獲取session中的手機(jī)驗(yàn)證碼
? ? ? ? HttpSession session = request.getSession();
? ? ? ? String sessionCode = (String)request.getSession().getAttribute(SmsController.SESSION_KEY);
? ? ? ? // 獲取用戶輸入的驗(yàn)證碼
? ? ? ? String inpuCode = request.getParameter("smsCode");
? ? ? ? //手機(jī)號(hào)
? ? ? ? String mobileInRequest = request.getParameter("mobile");
? ? ? ? if(StringUtils.isEmpty(mobileInRequest)){
? ? ? ? ? ? throw new ValidateCodeException("手機(jī)號(hào)碼不能為空氛什!");
? ? ? ? }
? ? ? ? if(StringUtils.isEmpty(inpuCode)){
? ? ? ? ? ? throw new ValidateCodeException("短信驗(yàn)證碼不能為空!");
? ? ? ? }
? ? ? ? if(StrUtil.isBlank(sessionCode)){
? ? ? ? ? ? throw new ValidateCodeException("短信驗(yàn)證碼不存在匪凉!");
? ? ? ? }
? ? ? ? if(!sessionCode.equalsIgnoreCase(inpuCode)){
? ? ? ? ? ? throw new ValidateCodeException("輸入的短信驗(yàn)證碼錯(cuò)誤枪眉!");
? ? ? ? }
? ? ? ? session.removeAttribute(SmsController.SESSION_KEY);
? ? }
}

實(shí)現(xiàn)手機(jī)認(rèn)證SmsCodeAuthenticationFilter過濾器

創(chuàng)建com.security.learn.filter.SmsCodeAuthenticationFilter,仿照UsernamePasswordAuthenticationFilter進(jìn)行代碼實(shí)現(xiàn),不過將用戶名再层、密碼換成手機(jī)號(hào)進(jìn)行認(rèn)證贸铜,短信驗(yàn)證碼在此部分已經(jīng)沒有用了,因?yàn)槲覀冊(cè)赟msCodeValidateFilter已經(jīng)驗(yàn)證過了聂受。

public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
? ? public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
? ? private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY ;? ? //請(qǐng)求中攜帶手機(jī)號(hào)的參數(shù)名稱
? ? private boolean postOnly = true;? ? //指定當(dāng)前過濾器是否只處理POST請(qǐng)求

? ? public SmsCodeAuthenticationFilter() {
? ? ? ? //指定當(dāng)前過濾器處理的請(qǐng)求
? ? ? ? super(new AntPathRequestMatcher("http://mobile/form", "POST"));
? ? }

? ? public Authentication attemptAuthentication(
? ? ? ? ? ? HttpServletRequest request,? ? ? ? ? ? HttpServletResponse response)
? ? ? ? ? ? throws AuthenticationException {
? ? ? ? if (this.postOnly && !request.getMethod().equals("POST")) {
? ? ? ? ? ? throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
? ? ? ? }
? ? ? ? //從請(qǐng)求中獲取手機(jī)號(hào)碼
? ? ? ? String mobile = this.obtainMobile(request);
? ? ? ? if (mobile == null) {
? ? ? ? ? ? mobile = "";
? ? ? ? }
? ? ? ? mobile = mobile.trim();
? ? ? ? SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
? ? ? ? this.setDetails(request, authRequest);

? ? ? ? return this.getAuthenticationManager().authenticate(authRequest);
? ? }
? ? /**
? ? * 從從請(qǐng)求中獲取手機(jī)號(hào)碼
? ? * @param request
? ? * @return? ? */
? ? protected String obtainMobile(HttpServletRequest request) {
? ? ? ? return request.getParameter(this.mobileParameter);
? ? }
? ? /**
? ? * 將請(qǐng)求中的Sessionid和host主句ip放到SmsCodeAuthenticationToken中
? ? * @param request
? ? * @param authRequest? ? */
? ? protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
? ? ? ? authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
? ? }
? ? public void setMobileParameter(String mobileParameter) {
? ? ? ? Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
? ? ? ? this.mobileParameter = mobileParameter;
? ? }
? ? public void setPostOnly(boolean postOnly) {
? ? ? ? this.postOnly = postOnly;
? ? }
? ? public final String getMobileParameter() {
? ? ? ? return this.mobileParameter;
? ? }
}

封裝手機(jī)認(rèn)證Token? SmsCodeAuthenticationToken

創(chuàng)建com.security.learn.filter.SmsCodeAuthenticationToken蒿秦,仿照UsernamePasswordAuthenticationToken進(jìn)行代碼實(shí)現(xiàn)

public class SmsCodeAuthenticationToken? extends AbstractAuthenticationToken {
? ? private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

? ? //存放認(rèn)證信息,認(rèn)證之前存放手機(jī)號(hào)蛋济,認(rèn)證之后存放登錄的用戶
? ? private final Object principal;

? ? /**
? ? * 開始認(rèn)證時(shí),SmsCodeAuthenticationToken 接收的是手機(jī)號(hào)碼, 并且 標(biāo)識(shí)未認(rèn)證
? ? * @param mobile? ? */
? ? public SmsCodeAuthenticationToken(String mobile) {
? ? ? ? super(null);
? ? ? ? this.principal = mobile;
? ? ? ? this.setAuthenticated(false);
? ? }

? ? /**
? ? *? 當(dāng)認(rèn)證通過后,會(huì)重新創(chuàng)建一個(gè)新的SmsCodeAuthenticationToken,來標(biāo)識(shí)它已經(jīng)認(rèn)證通過,
? ? * @param principal 用戶信息
? ? * @param authorities 用戶權(quán)限? ? */
? ? public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
? ? ? ? super(authorities);
? ? ? ? this.principal = principal;
? ? ? ? super.setAuthenticated(true);//表示認(rèn)證通過
? ? }

? ? /**
? ? * 在父類中是一個(gè)抽象方法,所以要實(shí)現(xiàn), 但是它是密碼,而當(dāng)前不需要,則直接返回null
? ? * @return? ? */
? ? public Object getCredentials() {
? ? ? ? return null;
? ? }

? ? /**
? ? * 手機(jī)號(hào)獲取
? ? * @return? ? */
? ? public Object getPrincipal() {
? ? ? ? return this.principal;
? ? }

? ? public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
? ? ? ? if (isAuthenticated) {
? ? ? ? ? ? throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
? ? ? ? }
? ? ? ? super.setAuthenticated(false);
? ? }

? ? public void eraseCredentials() {
? ? ? ? super.eraseCredentials();
? ? }
}

手機(jī)認(rèn)證提供者 SmsCodeAuthenticationProvider

創(chuàng)建com.security.learn.filter.SmsCodeAuthenticationProvider棍鳖,提供給底層的ProviderManager代碼實(shí)現(xiàn)如下

public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
? ? @Autowired
? ? @Qualifier("smsCodeUserDetailsService")
? ? private UserDetailsService userDetailsService;
? ? public UserDetailsService getUserDetailsService() {
? ? ? ? return userDetailsService;
? ? }
? ? public void setUserDetailsService(UserDetailsService userDetailsService) {
? ? ? ? this.userDetailsService = userDetailsService;
? ? }
? ? /**
? ? * 處理認(rèn)證:
? ? * 1. 通過 手機(jī)號(hào) 去數(shù)據(jù)庫查詢用戶信息(UserDeatilsService)
? ? * 2. 再重新構(gòu)建認(rèn)證信息
? ? * @param authentication
? ? * @return
? ? * @throws AuthenticationException? ? */
? ? @Override
? ? public Authentication authenticate(Authentication authentication) throws AuthenticationException {

? ? ? ? //利用UserDetailsService獲取用戶信息,拿到用戶信息后重新組裝一個(gè)已認(rèn)證的Authentication

? ? ? ? SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken)authentication;
? ? ? ? UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());? //根據(jù)手機(jī)號(hào)碼拿到用戶信息
? ? ? ? if(user == null){
? ? ? ? ? ? throw new AuthenticationServiceException("無法獲取用戶信息");
? ? ? ? }
? ? ? ? SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user,user.getAuthorities());
? ? ? ? authenticationResult.setDetails(authenticationToken.getDetails());
? ? ? ? return authenticationResult;
? ? }

? ? /**
? ? * AuthenticationManager挑選一個(gè)AuthenticationProvider
? ? * 來處理傳入進(jìn)來的Token就是根據(jù)supports方法來判斷的
? ? * @param aClass
? ? * @return? ? */
? ? @Override
? ? public boolean supports(Class<?> aClass) {
? ? ? ? return SmsCodeAuthenticationToken.class.isAssignableFrom(aClass);
? ? }
}

手機(jī)號(hào)獲取用戶信息 SmsCodeUserDetailsService

創(chuàng)建com.security.learn.impl.SmsCodeUserDetailsService類碗旅,不要注入PasswordEncoder

@Slf4j
@Component("smsCodeUserDetailsService")
public class SmsCodeUserDetailsService implements UserDetailsService {
? ? @Override
? ? public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
? ? ? ? log.info("請(qǐng)求的手機(jī)號(hào)是:" + mobile);
? ? ? ? return new User(mobile, "", true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
? ? }
}

因?yàn)闇y(cè)試就沒有去數(shù)據(jù)庫中獲取手機(jī)號(hào)

自定義管理認(rèn)證配置 SmsCodeSecurityConfig

最后我們將以上實(shí)現(xiàn)進(jìn)行組裝渡处,并將以上接口實(shí)現(xiàn)以配置的方式告知Spring Security。因?yàn)榕渲么a比較多祟辟,所以我們單獨(dú)抽取一個(gè)關(guān)于短信驗(yàn)證碼的配置類SmsCodeSecurityConfig医瘫,繼承自SecurityConfigurerAdapter。將上面定義的組件綁定起來旧困,添加到容器中:

注意添加@Component注解

@Component
public class SmsCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
? ?
? ? @Autowired
? ? @Qualifier("smsCodeUserDetailsService")
? ? private SmsCodeUserDetailsService smsCodeUserDetailsService;

? ? @Resource
? ? private SmsCodeValidateFilter? smsCodeValidateFilter;

? ? @Override
? ? public void configure(HttpSecurity http) throws Exception {

? ? ? ? //創(chuàng)建手機(jī)校驗(yàn)過濾器實(shí)例
? ? ? ? SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
? ? ? ? //接收 AuthenticationManager 認(rèn)證管理器
? ? ? ? smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
? ? ? ? //處理成功handler
? ? ? ? //smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
? ? ? ? //處理失敗handler
? ? ? ? //smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
? ? ? ? smsCodeAuthenticationFilter.setRememberMeServices(http.getSharedObject(RememberMeServices.class));
? ? ? ? // 獲取驗(yàn)證碼提供者
? ? ? ? SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
? ? ? ? smsCodeAuthenticationProvider.setUserDetailsService(smsCodeUserDetailsService);

? ? ? ? //在用戶密碼過濾器前面加入短信驗(yàn)證碼校驗(yàn)過濾器
? ? ? ? http.addFilterBefore(smsCodeValidateFilter, UsernamePasswordAuthenticationFilter.class);
? ? ? ? //在用戶密碼過濾器后面加入短信驗(yàn)證碼認(rèn)證授權(quán)過濾器
? ? ? ? http.authenticationProvider(smsCodeAuthenticationProvider)
? ? ? ? ? ? ? ? .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
? ? }
}

綁定到安全配置 LearnSrpingSecurity

  1. . 向 LearnSrpingSecurity中注入 SmsCodeValidateFilter和 SmsCodeSecurityConfig實(shí)例

  2. 將 SmsCodeValidateFilter實(shí)例添加到 UsernamePasswordAuthenticationFilter 前面

? http.csrf().disable() //禁用跨站csrf攻擊防御醇份,后面的章節(jié)會(huì)專門講解
? ? ? ? ? ? ? ? //.addFilterBefore(codeValidateFilter, UsernamePasswordAuthenticationFilter.class)
? ? ? ? ? ? ? ? .addFilterBefore(smsCodeValidateFilter, UsernamePasswordAuthenticationFilter.class)
  1. 在 LearnSrpingSecurity#confifigure(HttpSecurity http) 方法體最后調(diào)用 apply 添加 SmsCodeSecurityConfig
? ? ? ? ? ? ? ? .and()
? ? ? ? ? ? ? ? .apply(smsCodeSecurityConfig)

具體實(shí)現(xiàn)如圖

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

實(shí)現(xiàn)手機(jī)登錄RememberMe功能

實(shí)現(xiàn)分析

  1. UsernamePasswordAuthenticationFilter過濾器中有一個(gè)RememberMeServices引用,它的父類AbstractAuthenticationProcessingFilter吼具,提供提供的 setRememberMeServices方法被芳。

  2. 而在實(shí)現(xiàn)手機(jī)短信驗(yàn)證碼登錄時(shí),我們自定了一個(gè)? MobileAuthenticationFilter? 也一樣的繼承了AbstractAuthenticationProcessingFilter 它馍悟,我們只要向其 setRememberMeServices 方法手動(dòng)注入一個(gè) RememberMeServices 實(shí)例即可畔濒。

代碼實(shí)現(xiàn)

  1. com.security.learn.config.SmsCodeSecurityConfig中向SmsCodeAuthenticationFilter中注入RememberMeServices實(shí)例
 smsCodeAuthenticationFilter.setRememberMeServices(http.getSharedObject(RememberMeServices.class));
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  • 檢查 記住我 的 input 標(biāo)簽的 name="remember-me-test"
<span>記住我</span><input type="checkbox" name="remember-me-test" >? <br>
  • rememberMeParameter設(shè)置from表單“自動(dòng)登錄”勾選框的參數(shù)名稱。如果這里改了锣咒,from表單中checkbox的name屬性要對(duì)應(yīng)的更改侵状。如果不設(shè)置默認(rèn)是remember-me赞弥。
  • rememberMeCookieName設(shè)置了保存在瀏覽器端的cookie的名稱,如果不設(shè)置默認(rèn)也是remember-me趣兄。如下圖中查看瀏覽器的cookie绽左。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

測(cè)試

重啟項(xiàng)目,訪問 http://localhost:8888/mobile/page輸入手機(jī)號(hào)與驗(yàn)證碼, 勾選 記住我? , 點(diǎn)擊登錄

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

查看數(shù)據(jù)庫中中 persistent_logins 表的記錄

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

關(guān)閉瀏覽器, 再重新打開瀏覽器訪問http://localhost:8888 , 發(fā)現(xiàn)會(huì)跳轉(zhuǎn)回用戶名密碼登錄頁,而正常應(yīng)該勾選了 記住我? , 這一步應(yīng)該是可以正常訪問的.

錯(cuò)誤原因

數(shù)據(jù)庫中 username 為 手機(jī)號(hào) 1333383XXXX, 當(dāng)你訪問http://localhost:8888默認(rèn)RememberMeServices 是調(diào)

用 CustomUserDetailsService? 通過用戶名查詢, 而 當(dāng)前在 CustomUserDetailsService 判斷了用戶名為 admin才通

過認(rèn)證, 而此時(shí)傳入的用戶名是 1333383XXXX, 所以查詢不到 1333383XXXX用戶數(shù)據(jù)

錯(cuò)誤解決方式

數(shù)據(jù)庫中的 persistent_logins 表為什么存儲(chǔ)的是手機(jī)號(hào)?原因是當(dāng)前在 SmsCodeUserDetailsService中返回的 User 對(duì)象中的 username 屬性設(shè)置的是手機(jī)號(hào) mobile,而應(yīng)該設(shè)置這個(gè)手機(jī)號(hào)所對(duì)應(yīng)的那個(gè)用戶名. 比如當(dāng)前username 的值

@Slf4j
@Component("smsCodeUserDetailsService")
public class SmsCodeUserDetailsService implements UserDetailsService {
? ? @Override
? ? public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
? ? ? ? log.info("請(qǐng)求的手機(jī)號(hào)是:" + mobile);

? ? ? ? return new User("admin", "", true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
? ? }
}

:我們這里實(shí)際上是寫為固定admin了實(shí)際上需要通過數(shù)據(jù)庫根據(jù)手機(jī)號(hào)獲取用戶信息艇潭。

關(guān)閉瀏覽再打開訪問http://localhost:8888就無需手動(dòng)登錄認(rèn)證了拼窥。因?yàn)槟J(rèn)采用的 CustomUserDetailsService 查詢可查詢到用戶名為 admin 的信息,即認(rèn)證通過

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

如果您覺得本文不錯(cuò)蹋凝,歡迎關(guān)注,點(diǎn)贊,收藏支持鲁纠,您的關(guān)注是我堅(jiān)持的動(dòng)力!

原創(chuàng)不易鳍寂,轉(zhuǎn)載請(qǐng)注明出處改含,感謝支持!如果本文對(duì)您有用迄汛,歡迎轉(zhuǎn)發(fā)分享捍壤!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鞍爱,隨后出現(xiàn)的幾起案子鹃觉,更是在濱河造成了極大的恐慌,老刑警劉巖睹逃,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盗扇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡唯卖,警方通過查閱死者的電腦和手機(jī)粱玲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門躬柬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拜轨,“玉大人,你說我怎么就攤上這事允青¢夏耄” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵颠锉,是天一觀的道長(zhǎng)法牲。 經(jīng)常有香客問我,道長(zhǎng)琼掠,這世上最難降的妖魔是什么拒垃? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瓷蛙,結(jié)果婚禮上悼瓮,老公的妹妹穿的比我還像新娘戈毒。我一直安慰自己,他們只是感情好横堡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布埋市。 她就那樣靜靜地躺著,像睡著了一般命贴。 火紅的嫁衣襯著肌膚如雪道宅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天胸蛛,我揣著相機(jī)與錄音污茵,去河邊找鬼。 笑死胚泌,一個(gè)胖子當(dāng)著我的面吹牛省咨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玷室,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼零蓉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了穷缤?” 一聲冷哼從身側(cè)響起敌蜂,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎津肛,沒想到半個(gè)月后章喉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡身坐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年秸脱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片部蛇。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摊唇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涯鲁,到底是詐尸還是另有隱情巷查,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布抹腿,位于F島的核電站岛请,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏警绩。R本人自食惡果不足惜崇败,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肩祥。 院中可真熱鬧后室,春花似錦微渠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至松申,卻和暖如春云芦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贸桶。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國打工舅逸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皇筛。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓琉历,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親水醋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子旗笔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容