Spring security(四)-spring boot+spring securityメール認証+redis統合
60431 ワード
現在主流の登録方式は主に3種類ある:アカウントパスワード登録、メール認証コード登録と第三者授権登録、前節Spring security(3)—認証過程はspring securityアカウントパスワード方式登録を分析したが、今spring securityメール方式認証登録を分析する. Spring securityメール方式、IP検証などの類似モードの登録方式の検証は、アカウントパスワード方式の登録手順に基づいて書き換えることができ、主に以下の手順で展開される.
カスタムFilter:カスタムAuthenticationカスタムAuthenticationProviderカスタムUserDetailsService SecurityConfig構成カスタムフィルタ:
コンストラクタを構築し、コンストラクタで構成要求パスおよび要求方式のフィルタカスタムattemptAuthentication()認証ステップ2ステップで認証するにはAuthenticationProviderによる最終的な認証が必要であり、認証filterではAuthenticationProviderをfilterに設定する必要があり、AuthenticationProviderを管理するのはAuthenticationManagerである.そのため、フィルタfilterを作成するときにAuthenticationManagerを設定する必要があります.この手順の詳細は5.1 SecurityConfig構成手順です.
第2ステップにおいてattemptAuthentication()認証方法は主に以下のステップを行う.post要求認証; 2).requestは携帯電話番号と検証コードの取得を要求した. 3).カスタムAuthenticationオブジェクトで携帯電話番号と検証コードをカプセル化します. 4).AuthenticationManagerの使用authenticate()メソッドを検証します.カスタムfilter実装コード: Authentication:filterおよびその後の認証にはカスタムAuthenticationオブジェクトを使用する必要があります.カスタムAuthenticationオブジェクトはU s e r m e P a s s s s s w o r d AuthenticationTokenに基づいてシミュレーションされ、AbstractAuthenticationToken抽象クラスを実現できます.カスタムSmsAuthenticationToken:
3.AuthenticationProviderA b s t r a c t U s e r D e tailsAuthenticationProviderに従って、AuthenticationProviderおよびMessageSourceAwareインタフェースをシミュレートすることができます.認証ロジックは実装を定義できます.カスタムAuthenticationProvider:UserDetailsService AuthenticationProviderの最終認証ポリシーエントリでは、認証方式の実現ロジックはUserDetailsServiceである.独自のプロジェクトに基づいて認証ロジックをカスタマイズできます.カスタムUserDetailsServices:
5.SecurityConfig 5.1カスタムSmsショートメッセージ検証コンポーネント構成SecurityConfig カスタムコンポーネント構成SecurityConfigでは、A b s t r a c t u t h e n t i c a tionFilterConfigurer(サブクラスFormLoginConfigurer)に従ってSmsAuthenticationSecurityConfigを模写することができ、主に以下の構成を行う.
フィルタリングリンクにフィルタを追加し、フィルタ処理を実行するために、デフォルトのAuthenticationManager(定義可能)をカスタムfilterフィルタに設定します.カスタムのUserDetailsServiceをカスタムのAuthenticationProvideに設定します.(一般的にU s e r a m e P a sswordAuthenticationFilterの前に加算される)
SmsAuthenticationSecurityConfigを構成するには、次の手順に従います.
カスタムFilter:カスタムAuthenticationカスタムAuthenticationProviderカスタムUserDetailsService SecurityConfig構成
コンストラクタを構築し、コンストラクタで構成要求パスおよび要求方式のフィルタカスタムattemptAuthentication()認証ステップ2ステップで認証するにはAuthenticationProviderによる最終的な認証が必要であり、認証filterではAuthenticationProviderをfilterに設定する必要があり、AuthenticationProviderを管理するのはAuthenticationManagerである.そのため、フィルタfilterを作成するときにAuthenticationManagerを設定する必要があります.この手順の詳細は5.1 SecurityConfig構成手順です.
第2ステップにおいてattemptAuthentication()認証方法は主に以下のステップを行う.post要求認証; 2).requestは携帯電話番号と検証コードの取得を要求した. 3).カスタムAuthenticationオブジェクトで携帯電話番号と検証コードをカプセル化します. 4).AuthenticationManagerの使用authenticate()メソッドを検証します.カスタムfilter実装コード:
public class SmsAuthenticationfilter extends AbstractAuthenticationProcessingFilter {
private boolean postOnly = true;
public SmsAuthenticationfilter() {
super(new AntPathRequestMatcher(SecurityConstants.APP_MOBILE_LOGIN_URL, "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
Assert.hasText(SecurityConstants.MOBILE_NUMBER_PARAMETER, "mobile parameter must not be empty or null");
String mobile = request.getParameter(SecurityConstants.MOBILE_NUMBER_PARAMETER);
String smsCode = request.ge+tParameter(SecurityConstants.MOBILE_VERIFY_CODE_PARAMETER);
if (mobile == null) {
mobile="";
}
if(smsCode == null){
smsCode="";
}
mobile = mobile.trim();
smsCode = smsCode.trim();
SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile,smsCode);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}protected void setDetails(HttpServletRequest request,
SmsAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
}
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;
private Object credentials;
public SmsAuthenticationToken(Object principal,Object credentials ) {
super(null);
this.principal = principal;
this.credentials=credentials;
setAuthenticated(false);
}
public SmsAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) {
super(null);
this.principal = principal;
this.credentials=credentials;
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials=credentials;
}
@Override
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);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
3.AuthenticationProviderA b s t r a c t U s e r D e tailsAuthenticationProviderに従って、AuthenticationProviderおよびMessageSourceAwareインタフェースをシミュレートすることができます.認証ロジックは実装を定義できます.カスタムAuthenticationProvider:
public class SmsAuthenticationProvide implements AuthenticationProvider, MessageSourceAware {
private UserDetailsService userDetailsService;
private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
@Override
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
@Override
public Authentication authenticate(Authentication authentication) {
Assert.isInstanceOf(SmsAuthenticationToken.class, authentication,
messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;
// SecurityContext UserDetailsService
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authenticationToken);
String mobile = (String) authenticationToken.getPrincipal();
if (mobile == null) {
throw new InternalAuthenticationServiceException("can't obtain user info ");
}
mobile = mobile.trim();
//
UserDetails user = userDetailsService.loadUserByUsername(mobile);
if (user == null) {
throw new InternalAuthenticationServiceException("can't obtain user info ");
}
SmsAuthenticationToken smsAuthenticationToken = new SmsAuthenticationToken(user, user.getAuthorities());
return smsAuthenticationToken;
}
@Override
public boolean supports(Class<?> authentication) {
return (SmsAuthenticationToken.class.isAssignableFrom(authentication));
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
}
public class SmsUserDetailsService implements UserDetailsService {
@Autowired
private RedisUtil redisUtil;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// SecurityContext ( 、 )
SecurityContext context = SecurityContextHolder.getContext();
SmsAuthenticationToken authentication = (SmsAuthenticationToken) context.getAuthentication();
if(!additionalAuthenticationChecks(username,authentication)){
return null;
}
// ,
return new User("admin", "123456", Arrays.asList(new SimpleGrantedAuthority("admin")));
}
public boolean additionalAuthenticationChecks(String mobile, SmsAuthenticationToken smsAuthenticationToken) {
// redis value
String smsCode = redisUtil.get(mobile).toString();
//
String credentials = (String) smsAuthenticationToken.getCredentials();
if(StringUtils.isEmpty(credentials)){
return false;
}
if (credentials.equalsIgnoreCase(smsCode)) {
return true;
}
return false;
}
}
5.SecurityConfig 5.1カスタムSmsショートメッセージ検証コンポーネント構成SecurityConfig カスタムコンポーネント構成SecurityConfigでは、A b s t r a c t u t h e n t i c a tionFilterConfigurer(サブクラスFormLoginConfigurer)に従ってSmsAuthenticationSecurityConfigを模写することができ、主に以下の構成を行う.
フィルタリングリンクにフィルタを追加し、フィルタ処理を実行するために、デフォルトのAuthenticationManager(定義可能)をカスタムfilterフィルタに設定します.カスタムのUserDetailsServiceをカスタムのAuthenticationProvideに設定します.(一般的にU s e r a m e P a sswordAuthenticationFilterの前に加算される)
SmsAuthenticationSecurityConfigを構成するには、次の手順に従います.
```java
@Component
public class SmsAuthenticationSecurityConfig extends SecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(HttpSecurity http) throws Exception {
// SmsAuthenticationfilter,
SmsAuthenticationfilter smsAuthenticationfilter = new SmsAuthenticationfilter();
smsAuthenticationfilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
smsAuthenticationfilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler());
smsAuthenticationfilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler());
// SmsAuthenticationProvide
SmsAuthenticationProvide smsAuthenticationProvide=new SmsAuthenticationProvide();
smsAuthenticationProvide.setUserDetailsService(userDetailsService);
http.authenticationProvider(smsAuthenticationProvide);
//
http.addFilterAfter(smsAuthenticationfilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler() {
return new CustomAuthenticationSuccessHandler();
}
@Bean
public CustomAuthenticationFailureHandler customAuthenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
}
5.2 SecurityConfig
SecurityConfig Spring Security( )--WebSecurityConfigurer filter 。
SecurityConfig :
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;
@Autowired
private CustomAuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private CustomAuthenticationFailureHandler authenticationFailureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and()
.formLogin()
.loginPage(SecurityConstants.APP_FORM_LOGIN_PAGE)
// form URL
.loginProcessingUrl(SecurityConstants.APP_FORM_LOGIN_URL)
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and()
// smsAuthenticationSecurityConfig
.apply(smsAuthenticationSecurityConfig)
.and()
// URL
.authorizeRequests()
.antMatchers(SecurityConstants.APP_MOBILE_VERIFY_CODE_URL,
SecurityConstants.APP_USER_REGISTER_URL)
.permitAll()
.and()
.csrf().disable();
}
@Bean
public ObjectMapper objectMapper(){
return new ObjectMapper();
}
}
6.
6.1 redis
RedisUtil :
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
*
*
* @param key
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
*
*
* @param key
* @param value
* @return true false
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
*
*
* @param key
* @param value
* @param time ( ) time 0 time 0
* @return true false
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
redisConfig :
@Configuration
public class RedisConfig {
@Autowired
private RedisProperties properties;
@Bean
@SuppressWarnings("all")
@ConditionalOnClass(RedisConnectionFactory.class)
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key String
template.setKeySerializer(stringRedisSerializer);
// hash key String
template.setHashKeySerializer(stringRedisSerializer);
// value jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash value jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
@Qualifier("redisConnectionFactory")
public RedisConnectionFactory redisConnectionFactory(){
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName(properties.getHost());
redisConfig.setPort(properties.getPort());
redisConfig.setPassword(RedisPassword.of(properties.getPassword()));
redisConfig.setDatabase(properties.getDatabase());
//redis
JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();
if (this.properties.getTimeout() != null) {
Duration timeout = this.properties.getTimeout();
builder.readTimeout(timeout).connectTimeout(timeout);
}
RedisProperties.Pool pool = this.properties.getJedis().getPool();
if (pool != null) {
builder.usePooling().poolConfig(this.jedisPoolConfig(pool));
}
JedisClientConfiguration jedisClientConfiguration = builder.build();
// JedisConnectionFactory
return new JedisConnectionFactory(redisConfig,jedisClientConfiguration);
}
private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(pool.getMaxActive());
config.setMaxIdle(pool.getMaxIdle());
config.setMinIdle(pool.getMinIdle());
if (pool.getMaxWait() != null) {
config.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
return config;
}
}