欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Security 實(shí)現(xiàn)短信驗(yàn)證碼登錄功能

 更新時(shí)間:2020年05月20日 14:32:29   作者:木兮同學(xué)  
這篇文章主要介紹了Spring Security 實(shí)現(xiàn)短信驗(yàn)證碼登錄功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

之前文章都是基于用戶名密碼登錄,第六章圖形驗(yàn)證碼登錄其實(shí)還是用戶名密碼登錄,只不過(guò)多了一層圖形驗(yàn)證碼校驗(yàn)而已;Spring Security默認(rèn)提供的認(rèn)證流程就是用戶名密碼登錄,整個(gè)流程都已經(jīng)固定了,雖然提供了一些接口擴(kuò)展,但是有些時(shí)候我們就需要有自己特殊的身份認(rèn)證邏輯,比如用短信驗(yàn)證碼登錄,它和用戶名密碼登錄的邏輯是不一樣的,這時(shí)候就需要重新寫(xiě)一套身份認(rèn)證邏輯。

開(kāi)發(fā)短信驗(yàn)證碼接口

獲取驗(yàn)證碼

短信驗(yàn)證碼的發(fā)送獲取邏輯和圖片驗(yàn)證碼類(lèi)似,這里直接貼出代碼。

@GetMapping("/code/sms")
	public void createSmsCode(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 創(chuàng)建驗(yàn)證碼
		ValidateCode smsCode = createCodeSmsCode(request);
		// 將驗(yàn)證碼放到session中
		sessionStrategy.setAttribute(new ServletWebRequest(request), SMS_CODE_SESSION_KEY, smsCode);
		String mobile = ServletRequestUtils.getRequiredStringParameter(request, "mobile");
		// 發(fā)送驗(yàn)證碼
		smsCodeSender.send(mobile, smsCode.getCode());
	}

前端代碼

<tr>
				<td>手機(jī)號(hào):</td>
				<td><input type="text" name="mobile" value="13012345678"></td>
			</tr>
			<tr>
				<td>短信驗(yàn)證碼:</td>
				<td>
					<input type="text" name="smsCode">
					<a href="/code/sms?mobile=13012345678" rel="external nofollow" >發(fā)送驗(yàn)證碼</a>
				</td>
			</tr>

短信驗(yàn)證碼流程原理

短信驗(yàn)證碼登錄和用戶名密碼登錄對(duì)比

流程對(duì)比

步驟流程

  • 首先點(diǎn)擊登錄應(yīng)該會(huì)被SmsAuthenticationFilter過(guò)濾器處理,這個(gè)過(guò)濾器拿到請(qǐng)求以后會(huì)在登錄請(qǐng)求中拿到手機(jī)號(hào),然后封裝成自定義的一個(gè)SmsAuthenticationToken(未認(rèn)證)。
  • 這個(gè)Token也會(huì)傳給AuthenticationManager,因?yàn)?code>AuthenticationManager整個(gè)系統(tǒng)只有一個(gè),它會(huì)檢索系統(tǒng)中所有的AuthenticationProvider,這時(shí)候我們要提供自己的SmsAuthenticationProvider,用它來(lái)校驗(yàn)自己寫(xiě)的SmsAuthenticationToken的手機(jī)號(hào)信息。
  • 在校驗(yàn)的過(guò)程中同樣會(huì)調(diào)用UserDetailsService,把手機(jī)號(hào)傳給它讓它去讀用戶信息,去判斷是否能登錄,登錄成功的話再把SmsAuthenticationToken標(biāo)記為已認(rèn)證。
  • 到這里為止就是短信驗(yàn)證碼的認(rèn)證流程,上面的流程并沒(méi)有提到校驗(yàn)驗(yàn)證碼信息,其實(shí)它的驗(yàn)證流程和圖形驗(yàn)證碼驗(yàn)證流程也是類(lèi)似,同樣是在SmsAuthenticationFilter過(guò)濾器之前加一個(gè)過(guò)濾器來(lái)驗(yàn)證短信驗(yàn)證碼

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

SmsCodeAuthenticationToken

  • 作用:封裝認(rèn)證Token
  • 實(shí)現(xiàn):可以繼承AbstractAuthenticationToken抽象類(lèi),該類(lèi)實(shí)現(xiàn)了Authentication接口
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
	private final Object principal;
	/**
	 * 進(jìn)入SmsAuthenticationFilter時(shí),構(gòu)建一個(gè)未認(rèn)證的Token
	 *
	 * @param mobile
	 */
	public SmsCodeAuthenticationToken(String mobile) {
		super(null);
		this.principal = mobile;
		setAuthenticated(false);
	}
	/**
	 * 認(rèn)證成功以后構(gòu)建為已認(rèn)證的Token
	 *
	 * @param principal
	 * @param authorities
	 */
	public SmsCodeAuthenticationToken(Object principal,
			Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		super.setAuthenticated(true);
	}
	@Override
	public Object getCredentials() {
		return null;
	}
	@Override
	public Object getPrincipal() {
		return this.principal;
	}
	@Override
	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);
	}
	@Override
	public void eraseCredentials() {
		super.eraseCredentials();
	}
}

SmsCodeAuthenticationFilter

  • 作用:處理短信登錄的請(qǐng)求,構(gòu)建Token,把請(qǐng)求信息設(shè)置到Token中。
  • 實(shí)現(xiàn):該類(lèi)可以模仿UsernamePasswordAuthenticationFilter類(lèi),繼承AbstractAuthenticationProcessingFilter抽象類(lèi)
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
	private String mobileParameter = "mobile";
	private boolean postOnly = true;
 /**
 * 表示要處理的請(qǐng)求路徑
 */
	public SmsCodeAuthenticationFilter() {
 super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
	}
 @Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		String mobile = obtainMobile(request);
		if (mobile == null) {
			mobile = "";
		}
		mobile = mobile.trim();
		SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
		// 把請(qǐng)求信息設(shè)到Token中
		setDetails(request, authRequest);
		return this.getAuthenticationManager().authenticate(authRequest);
	}
	/**
	 * 獲取手機(jī)號(hào)
	 */
	protected String obtainMobile(HttpServletRequest request) {
		return request.getParameter(mobileParameter);
	}
	protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
		authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	}
	public void setMobileParameter(String usernameParameter) {
		Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
		this.mobileParameter = usernameParameter;
	}
	public void setPostOnly(boolean postOnly) {
		this.postOnly = postOnly;
	}
	public final String getMobileParameter() {
		return mobileParameter;
	}
}

SmsAuthenticationProvider

  • 作用:提供認(rèn)證Token的校驗(yàn)邏輯,配置為能夠支持SmsCodeAuthenticationToken的校驗(yàn)
  • 實(shí)現(xiàn):實(shí)現(xiàn)AuthenticationProvider接口,實(shí)現(xiàn)其兩個(gè)方法。
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
	private UserDetailsService userDetailsService;
 /**
 * 進(jìn)行身份認(rèn)證的邏輯
 *
 * @param authentication
 * @return
 * @throws AuthenticationException
 */
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
		
		UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
		if (user == null) {
			throw new InternalAuthenticationServiceException("無(wú)法獲取用戶信息");
		}
		
		SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());
		
		authenticationResult.setDetails(authenticationToken.getDetails());
		return authenticationResult;
	}
 /**
 * 表示支持校驗(yàn)的Token,這里是SmsCodeAuthenticationToken
 *
 * @param authentication
 * @return
 */
	@Override
	public boolean supports(Class<?> authentication) {
		return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
	}
	public UserDetailsService getUserDetailsService() {
		return userDetailsService;
	}
	public void setUserDetailsService(UserDetailsService userDetailsService) {
		this.userDetailsService = userDetailsService;
	}
}

ValidateCodeFilter

  • :校驗(yàn)短信驗(yàn)證碼
  • 實(shí)現(xiàn):和圖形驗(yàn)證碼類(lèi)似,繼承OncePerRequestFilter接口防止多次調(diào)用,主要就是驗(yàn)證碼驗(yàn)證邏輯,驗(yàn)證通過(guò)則繼續(xù)下一個(gè)過(guò)濾器。
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
	/**
	 * 驗(yàn)證碼校驗(yàn)失敗處理器
	 */
	@Autowired
	private AuthenticationFailureHandler authenticationFailureHandler;
	/**
	 * 系統(tǒng)配置信息
	 */
	@Autowired
	private SecurityProperties securityProperties;
	/**
	 * 系統(tǒng)中的校驗(yàn)碼處理器
	 */
	@Autowired
	private ValidateCodeProcessorHolder validateCodeProcessorHolder;
	/**
	 * 存放所有需要校驗(yàn)驗(yàn)證碼的url
	 */
	private Map<String, ValidateCodeType> urlMap = new HashMap<>();
	/**
	 * 驗(yàn)證請(qǐng)求url與配置的url是否匹配的工具類(lèi)
	 */
	private AntPathMatcher pathMatcher = new AntPathMatcher();
	/**
	 * 初始化要攔截的url配置信息
	 */
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
		urlMap.put("/authentication/mobile", ValidateCodeType.SMS);
		addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);
	}
	/**
	 * 講系統(tǒng)中配置的需要校驗(yàn)驗(yàn)證碼的URL根據(jù)校驗(yàn)的類(lèi)型放入map
	 * 
	 * @param urlString
	 * @param type
	 */
	protected void addUrlToMap(String urlString, ValidateCodeType type) {
		if (StringUtils.isNotBlank(urlString)) {
			String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlString, ",");
			for (String url : urls) {
				urlMap.put(url, type);
			}
		}
	}
	/**
	 * 驗(yàn)證短信驗(yàn)證碼
	 * 
	 * @param request
	 * @param response
	 * @param chain
	 * @throws ServletException
	 * @throws IOException
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		ValidateCodeType type = getValidateCodeType(request);
		if (type != null) {
			logger.info("校驗(yàn)請(qǐng)求(" + request.getRequestURI() + ")中的驗(yàn)證碼,驗(yàn)證碼類(lèi)型" + type);
			try {
				// 進(jìn)行驗(yàn)證碼的校驗(yàn)
				validateCodeProcessorHolder.findValidateCodeProcessor(type)
						.validate(new ServletWebRequest(request, response));
				logger.info("驗(yàn)證碼校驗(yàn)通過(guò)");
			} catch (ValidateCodeException exception) {
				// 如果校驗(yàn)拋出異常,則交給我們之前文章定義的異常處理器進(jìn)行處理
				authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
				return;
			}
		}
		// 繼續(xù)調(diào)用后邊的過(guò)濾器
		chain.doFilter(request, response);
	}
	/**
	 * 獲取校驗(yàn)碼的類(lèi)型,如果當(dāng)前請(qǐng)求不需要校驗(yàn),則返回null
	 * 
	 * @param request
	 * @return
	 */
	private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
		ValidateCodeType result = null;
		if (!StringUtils.equalsIgnoreCase(request.getMethod(), "GET")) {
			Set<String> urls = urlMap.keySet();
			for (String url : urls) {
				if (pathMatcher.match(url, request.getRequestURI())) {
					result = urlMap.get(url);
				}
			}
		}
		return result;
	}
}

添加配置

SmsCodeAuthenticationSecurityConfig

作用:配置SmsCodeAuthenticationFilter,后面需要把這些配置加到主配置類(lèi)BrowserSecurityConfig

@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
	
	@Autowired
	private AuthenticationSuccessHandler meicloudAuthenticationSuccessHandler;
	
	@Autowired
	private AuthenticationFailureHandler meicloudAuthenticationFailureHandler;
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
	private PersistentTokenRepository persistentTokenRepository;
	
	@Override
	public void configure(HttpSecurity http) throws Exception {
		
		SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
		// 設(shè)置AuthenticationManager
		smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
		// 設(shè)置登錄成功處理器
		smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(meicloudAuthenticationSuccessHandler);
		// 設(shè)置登錄失敗處理器
		smsCodeAuthenticationFilter.setAuthenticationFailureHandler(meicloudAuthenticationFailureHandler);
		String key = UUID.randomUUID().toString();
		smsCodeAuthenticationFilter.setRememberMeServices(new PersistentTokenBasedRememberMeServices(key, userDetailsService, persistentTokenRepository));
		
		SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
		smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
		// 將自己寫(xiě)的Provider加到Provider集合里去
		http.authenticationProvider(smsCodeAuthenticationProvider)
			.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
	}
}

BrowserSecurityConfig

作用:主配置類(lèi);添加短信驗(yàn)證碼配置類(lèi)、添加SmsCodeAuthenticationSecurityConfig配置

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	@Autowired
	private SecurityProperties securityProperties;
	@Autowired
	private DataSource dataSource;
	@Autowired
	private UserDetailsService userDetailsService;
	@Autowired
	private AuthenticationSuccessHandler meicloudAuthenticationSuccessHandler;
	@Autowired
	private AuthenticationFailureHandler meicloudAuthenticationFailureHandler;
	@Autowired
	private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// 驗(yàn)證碼校驗(yàn)過(guò)濾器
		ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
		// 將驗(yàn)證碼校驗(yàn)過(guò)濾器加到 UsernamePasswordAuthenticationFilter 過(guò)濾器之前
		http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
				.formLogin()
				// 當(dāng)用戶登錄認(rèn)證時(shí)默認(rèn)跳轉(zhuǎn)的頁(yè)面
				.loginPage("/authentication/require")
				// 以下這行 UsernamePasswordAuthenticationFilter 會(huì)知道要處理表單的 /authentication/form 請(qǐng)求,而不是默認(rèn)的 /login
				.loginProcessingUrl("/authentication/form")
				.successHandler(meicloudAuthenticationSuccessHandler)
				.failureHandler(meicloudAuthenticationFailureHandler)
				// 配置記住我功能
				.and()
				.rememberMe()
				// 配置TokenRepository
				.tokenRepository(persistentTokenRepository())
				// 配置Token過(guò)期時(shí)間
				.tokenValiditySeconds(3600)
				// 最終拿到用戶名之后,使用UserDetailsService去做登錄
				.userDetailsService(userDetailsService)
				.and()
				.authorizeRequests()
				// 排除對(duì) "/authentication/require" 和 "/meicloud-signIn.html" 的身份驗(yàn)證
				.antMatchers("/authentication/require", securityProperties.getBrowser().getSignInPage(), "/code/*").permitAll()
				// 表示所有請(qǐng)求都需要身份驗(yàn)證
				.anyRequest()
				.authenticated()
				.and()
				.csrf().disable()// 暫時(shí)把跨站請(qǐng)求偽造的功能關(guān)閉掉
				// 相當(dāng)于把smsCodeAuthenticationSecurityConfig里的配置加到上面這些配置的后面
				.apply(smsCodeAuthenticationSecurityConfig);
	}
	/**
	 * 記住我功能的Token存取器配置
	 *
	 * @return
	 */
	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
		tokenRepository.setDataSource(dataSource);
		// 啟動(dòng)的時(shí)候自動(dòng)創(chuàng)建表,建表語(yǔ)句 JdbcTokenRepositoryImpl 已經(jīng)都寫(xiě)好了
		tokenRepository.setCreateTableOnStartup(true);
		return tokenRepository;
	}
}

總結(jié)

到此這篇關(guān)于Spring Security 實(shí)現(xiàn)短信驗(yàn)證碼登錄功能的文章就介紹到這了,更多相關(guān)spring security 驗(yàn)證碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 快速校驗(yàn)實(shí)體類(lèi)時(shí),@Valid,@Validated,@NotNull注解無(wú)效的解決

    快速校驗(yàn)實(shí)體類(lèi)時(shí),@Valid,@Validated,@NotNull注解無(wú)效的解決

    這篇文章主要介紹了快速校驗(yàn)實(shí)體類(lèi)時(shí),@Valid,@Validated,@NotNull注解無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 解決因jdk版本引起的TypeNotPresentExceptionProxy異常

    解決因jdk版本引起的TypeNotPresentExceptionProxy異常

    這篇文章介紹了解決因jdk版本引起的TypeNotPresentExceptionProxy異常的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • Java Swing JSlider滑塊的實(shí)現(xiàn)示例

    Java Swing JSlider滑塊的實(shí)現(xiàn)示例

    這篇文章主要介紹了Java Swing JSlider滑塊的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 關(guān)于springboot中的自定義配置項(xiàng)

    關(guān)于springboot中的自定義配置項(xiàng)

    這篇文章主要介紹了關(guān)于springboot中的自定義配置項(xiàng),在項(xiàng)目開(kāi)發(fā)的過(guò)程中,經(jīng)常需要自定義系統(tǒng)業(yè)務(wù)方面的配置文件及配置項(xiàng),Spring Boot提供了@value注解、@ConfigurationProperties注解和Environment接口等3種方式自定義配置項(xiàng),需要的朋友可以參考下
    2023-07-07
  • Java8 Lamda方法引用和構(gòu)造引用原理

    Java8 Lamda方法引用和構(gòu)造引用原理

    這篇文章主要介紹了Java8 Lamda方法引用和構(gòu)造引用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Mybatis環(huán)境搭建及文件配置過(guò)程解析

    Mybatis環(huán)境搭建及文件配置過(guò)程解析

    這篇文章主要介紹了Mybatis環(huán)境搭建及文件配置過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot中的Thymeleaf用法

    SpringBoot中的Thymeleaf用法

    Thymeleaf是最近SpringBoot推薦支持的模板框架。本文重點(diǎn)給大家介紹SpringBoot中的Thymeleaf用法,需要的的朋友參考下吧
    2017-05-05
  • Spring Boot郵箱鏈接注冊(cè)驗(yàn)證及注冊(cè)流程

    Spring Boot郵箱鏈接注冊(cè)驗(yàn)證及注冊(cè)流程

    這篇文章給大家介紹Spring Boot郵箱鏈接注冊(cè)驗(yàn)證問(wèn)題及注冊(cè)流程分析,通過(guò)實(shí)例代碼給大家分享實(shí)現(xiàn)過(guò)程,感興趣的朋友跟隨小編一起看看吧
    2021-07-07
  • Java 8函數(shù)式接口Function BiFunction DoubleFunction區(qū)別

    Java 8函數(shù)式接口Function BiFunction DoubleFunction

    這篇文章主要為大家介紹了Java 8函數(shù)式接口Function BiFunction DoubleFunction區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • SpringBoot使用PageHelper插件實(shí)現(xiàn)Mybatis分頁(yè)效果

    SpringBoot使用PageHelper插件實(shí)現(xiàn)Mybatis分頁(yè)效果

    這篇文章主要介紹了SpringBoot使用PageHelper插件實(shí)現(xiàn)Mybatis分頁(yè)效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-02-02

最新評(píng)論