Spring Security 사용자 정의 문자 로그 인 인증 실현

사용자 정의 로그 인 filter
위의 글 에서 사용자 의 로그 인 에 대해 security 는 filter 차단 login 경 로 를 정의 하여 이 루어 집 니 다.따라서 사용자 정의 로그 인 을 실현 하려 면 filter 를 정의 하고 Abstract AuthenticationProcessingFilter 를 계승 하여 request 에서 핸드폰 번호 와 인증 코드 를 추출 한 다음 AuthenticationManager 에 제출 해 야 합 니 다.

public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 public static final String SPRING_SECURITY_FORM_PHONE_KEY = "phone";
 public static final String SPRING_SECURITY_FORM_VERIFY_CODE_KEY = "verifyCode";
 private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/smsLogin",
   "POST");
 protected SmsAuthenticationFilter() {
  super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
 }

 @Override
 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
  String phone = request.getParameter(SPRING_SECURITY_FORM_PHONE_KEY);
  String verifyCode = request.getParameter(SPRING_SECURITY_FORM_VERIFY_CODE_KEY);
  if (StringUtils.isBlank(phone)){
   phone = "";
  }
  if (StringUtils.isBlank(verifyCode)){
   verifyCode = "";
  }
  SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(phone, verifyCode);
  setDetails(request,authenticationToken);
  return getAuthenticationManager().authenticate(authenticationToken);
 }

 protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) {
  authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
 }
}

그 중에서 SmsAuthenticationToken 은 UsernamePassword AuthenticationToken 을 참조 하여 이 루어 집 니 다.

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(Collection<? extends GrantedAuthority> authorities, Object principal, Object credentials) {
  super(authorities);
  this.principal = principal;
  this.credentials = credentials;
  setAuthenticated(true);
 }

 @Override
 public Object getCredentials() {
  return credentials;
 }

 @Override
 public Object getPrincipal() {
  return principal;
 }
}

사용자 정의 provider 인증 실현
AuthenticationManager 가 최종 적 으로 Provider 에 인증 을 의뢰 할 것 이라는 것 을 알 고 있 기 때문에 인증 코드 가 정확 한 지 판단 하려 면 Provider 를 사용자 정의 해 야 합 니 다.

@Slf4j
@Component
public class SmsAuthenticationProvider implements AuthenticationProvider {
 @Autowired
 private UserDetailsService userDetailsService;

 @Override
 public Authentication authenticate(Authentication authentication) {
  Assert.isInstanceOf(SmsAuthenticationToken.class, authentication,
    () -> "SmsAuthenticationProvider.onlySupports Only SmsAuthenticationToken is supported");
  SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;
  String phone = (String) authenticationToken.getPrincipal();
  String verifyCode = (String) authenticationToken.getCredentials();
  UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
  if (userDetails == null){
   throw new InternalAuthenticationServiceException("cannot get user info");
  }
  //       
  if (!StringUtils.equals(CacheUtil.getValue(phone),verifyCode)){
   throw new AuthenticationCredentialsNotFoundException("     ");
  }
  return new SmsAuthenticationToken(userDetails.getAuthorities(),userDetails,verifyCode);
 }

 @Override
 public boolean supports(Class<?> authentication) {
  return authentication.isAssignableFrom(SmsAuthenticationToken.class);
 }
}

위의 CacheUtil 은 포 장 된 guava cache 의 실현 입 니 다.아 날로 그 인증 코드 를 메모리 에 저장 하고 이 곳 에서 대 비 를 합 니 다.대비 에 실패 하면 이상 을 던 지고 대비 에 성공 하면 새로운 token 으로 돌아 갑 니 다.이 token 에는 사용자 가 가 진 권한 이 포함 되 어 있 습 니 다.

@Slf4j
public class CacheUtil {
 private static final LoadingCache<String, String> CACHE = CacheBuilder.newBuilder()
   //      :   100 
   .maximumSize(100)
   //    :     1       
   .expireAfterWrite(1, TimeUnit.MINUTES)
   //               ,   CacheLoader load      
   .build(new CacheLoader<String, String>() {
    @Override
    public String load(String key) throws Exception {
     log.debug("      : {}",key);
     return "";
    }
   });
 public static void putValue(String key, String value){
  CACHE.put(key,value);
 }

 public static String getValue(String key){
  try {
   return CACHE.get(key);
  } catch (ExecutionException e) {
   e.printStackTrace();
  }
  return "";
 }
}

인증 결과 리 셋
filter 는 핸드폰 번호 와 인증 코드 를 provider 에 게 건 네 주 고 provider 의 검 사 를 통 해 결 과 는 두 가지 가 있 습 니 다.하 나 는 검증 에 성 공 했 고 하 나 는 검증 에 실 패 했 습 니 다.이 두 가지 서로 다른 결과 에 대해 우 리 는 두 개의 handler 를 실현 하고 결 과 를 얻 은 후에 리 셋 을 해 야 합 니 다.우 리 는 url 점프 만 간단하게 하기 때문에 Simple Url Authentication SuccessHandler 를 계승 해 야 합 니 다.
성공 에 대한:

@Component
public class SmsAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
 public SmsAuthSuccessHandler() {
  super("/index");
 }
}
failure 에 대한:

@Component
public class SmsAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
 public SmsAuthFailureHandler() {
  super("/failure");
 }
}
위의 전체 로그 인 프로 세 스 의 구성 요소 가 완성 되 었 으 니,그 다음 에는 그것들 을 통합 시 켜 야 한다.
통합 로그 인 구성 요소
구체 적 으로 어떻게 통합 하 는 지,우 리 는 폼 로그 인 에서 UsernamePassword AuthenticationFilter 가 어떻게 통합 되 었 는 지 참고 할 수 있 습 니 다.설정 클래스 로 돌아 가면 우리 가 Security 를 어떻게 설정 하 는 지 기억 하 십 니까?

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.formLogin()
    .loginPage("/login") //    
    .successForwardUrl("/index") //        
    .failureForwardUrl("/failure") //        
    .and()
    //   URL   
    .authorizeRequests()
    //            
    .antMatchers("/login")
    .permitAll()
    //    ,           
    .anyRequest()
    .authenticated()
    .and()
    //   csrf
    .csrf().disable();
 }
}
분석 폼 로그 인 실현
첫 번 째 문장 을 보고 http.formLogin()을 호출 했 습 니 다.HttpSecurity 의 formLogin 방법 은 다음 과 같 습 니 다.

public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
 return getOrApply(new FormLoginConfigurer<>());
}
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer)
 throws Exception {
  //    configure SecurityConfigurerAdapter
 C existingConfig = (C) getConfigurer(configurer.getClass());
 if (existingConfig != null) {
 return existingConfig;
 }
 return apply(configurer);
}
apply 방법 은 AbstractConfigured Security Builder 의 방법 입 니 다.우 리 는 현재 그것 의 실현 에 관심 을 가지 지 않 고 나중에 자세히 설명 할 것 입 니 다.이 방법 만 알 면 configurer 를 security 설정 에 추가 할 수 있 습 니 다.
이 곳 에 FormLoginConfigurer 클래스 를 추 가 했 습 니 다.이 클래스 에 대한 공식 설명 은 다음 과 같 습 니 다.
Adds form based authentication. All attributes have reasonable defaults making all parameters are optional. If no {@link #loginPage(String)} is specified, a default login page will be generated by the framework.
번역 하면:
폼 기반 인증 추가.모든 속성 은 합 리 적 인 기본 값 이 있어 서 모든 매개 변 수 를 선택 할 수 있 습 니 다.loginPage 가 지정 되 지 않 으 면 프레임 워 크 는 기본 로그 인 페이지 를 생 성 합 니 다.
그것 의 구조 방법 을 살 펴 보 자.

public FormLoginConfigurer() {
 super(new UsernamePasswordAuthenticationFilter(), null);
 usernameParameter("username");
 passwordParameter("password");
}
UsernamePassword Authentication Filter 가 부모 클래스 에 전 달 된 것 을 발 견 했 습 니 다.부모 클래스 Abstract Authentication Filter Configure 에 가 보 겠 습 니 다.

public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
 extends AbstractHttpConfigurer<T, B> {
 
 protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {
 this();
  //  filter  UsernamePasswordAuthenticationFilter
 this.authFilter = authenticationFilter;
 if (defaultLoginProcessingUrl != null) {
 loginProcessingUrl(defaultLoginProcessingUrl);
 }
 }
 
 @Override
 public void configure(B http) throws Exception {
 PortMapper portMapper = http.getSharedObject(PortMapper.class);
 if (portMapper != null) {
 this.authenticationEntryPoint.setPortMapper(portMapper);
 }
 RequestCache requestCache = http.getSharedObject(RequestCache.class);
 if (requestCache != null) {
 this.defaultSuccessHandler.setRequestCache(requestCache);
 }
  //  getSharedObject      。     AuthenticationManager
 this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
  //          
 this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
 this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
 if (this.authenticationDetailsSource != null) {
 this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
 }
 SessionAuthenticationStrategy sessionAuthenticationStrategy = http
 .getSharedObject(SessionAuthenticationStrategy.class);
 if (sessionAuthenticationStrategy != null) {
 this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
 }
 RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
 if (rememberMeServices != null) {
 this.authFilter.setRememberMeServices(rememberMeServices);
 }
 F filter = postProcess(this.authFilter);
  //  filter
 http.addFilter(filter);
 }
}
이곳 에서 주로 세 가지 일 을 한 것 을 볼 수 있다.
AuthenticationManager 를 filter 에 설정 합 니 다성공/실패 의 반전 을 추가 합 니 다필터 체인 에 필 터 를 추가 합 니 다폼 에 따라 로그 인하 여 설정 클래스 구현
위의 세 단 계 를 본 떠 서 우 리 는 스스로 설정 클래스 를 실현 할 수 있 습 니 다.Abstract Authentication FilterConfigurer 의 클래스 계승 관 계 를 볼 수 있 습 니 다.

그것 의 맨 위 에 있 는 최상 위 부 류 는 Security Configurer Adapter 입 니 다.우 리 는 그것 을 계승 하여 우리 의 기본 적 인 설정 을 실현 하면 됩 니 다.(AbstractHttpConfigurer 를 계승 할 수도 있 고 차별 하지 않 는 다 는 뜻)그리고 위의 세 단 계 를 실현 할 수 있 습 니 다.

@Component
public class SmsAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
 @Autowired
 private SmsAuthSuccessHandler smsAuthSuccessHandler;
 @Autowired
 private SmsAuthFailureHandler smsAuthFailureHandler;
 @Autowired
 private SmsAuthenticationProvider smsAuthenticationProvider;
 @Override
 public void configure(HttpSecurity builder) throws Exception {
  SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();
  smsAuthenticationFilter.setAuthenticationManager(builder.getSharedObject(AuthenticationManager.class));
  smsAuthenticationFilter.setAuthenticationSuccessHandler(smsAuthSuccessHandler);
  smsAuthenticationFilter.setAuthenticationFailureHandler(smsAuthFailureHandler);

  builder.authenticationProvider(smsAuthenticationProvider);
  builder.addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 }
}
위 와 는 조금 다 릅 니 다.사용자 정의 filter 는 순 서 를 지정 해 야 합 니 다.addFilterAfter 방법 을 통 해 필터 체인 에 필 터 를 추가 하고 사용자 정의 provider 도 함께 설정 해 야 합 니 다.
보안 에 설정 추가
이렇게 하면 우리 의 모든 구성 요 소 는 이미 한데 조합 되 었 습 니 다.설정 클래스 를 수정 하 십시오.

@Autowired
private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
 http.formLogin()
  .loginPage("/login")
  .and()
  .apply(smsAuthenticationSecurityConfig)
  .and()
  //   URL   
  .authorizeRequests()
  //            
  .antMatchers("/login","/verifyCode","/smsLogin","/failure")
  .permitAll()
  // anyRequest()      authenticated()      
  .anyRequest()
  .authenticated()
  .and()
  //   csrf
  .csrf().disable();
}
로그 인 페이지 의 로그 인 인터페이스 와 필드 이름 을 다시 수정 합 니 다:

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8">
 <title>login</title>
</head>
<body>
 <form action="/smsLogin" method="post">
  <input type="text" name="phone"/>
  <input type="password" name="verifyCode"/>
  <input type="submit" value="  "/>
 </form>
</body>
</html>
이렇게 문자 인증 코드 를 통 해 로그 인 하 는 기능 은 이미 실현 되 었 다.
사용자 정의 메 일 인증 코드 로그 인 을 다시 실현 하여 이미 지 를 강화 하 는 것 을 권장 합 니 다.
소스 코드 분석
configurer 설정 클래스 작업 원리
위 에 서 는 간단 한 사용 일 뿐 입 니 다.다음은 configure 가 어떻게 일 하 는 지 분석 하 겠 습 니 다.
여러분,아 이 디 어 를 열 어서 소스 코드 를 따라 가 셔 야 돼 요.
사실 위의 설정 을 통 해 알 수 있 듯 이 security 에 있 는 필 터 는 각종 xxxconfigure 를 통 해 설정 되 어 있 습 니 다.filter 는 설정 류 와 연결 되 어 있 는 것 으로 간단하게 이해 할 수 있 습 니 다.이 개념 을 깨 닫 고 우 리 는 계속 분석 했다.
위의 Abstract Authentication FilterConfigurer 의 클래스 계승 관계 도 를 보면 맨 위 에서 부터 분석 하면 Security Builder 와 Security Configurer 는 모두 인터페이스 입 니 다.

public interface SecurityBuilder<O> {
 /**
 *          
 */
 O build() throws Exception;
}
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
 /**
 *    
 */
 void init(B builder) throws Exception;
 void configure(B builder) throws Exception;

}

보안 ConfigurerAdapter 분석
위의 두 인터페이스의 구체 적 인 실현 은 Security Configurer Adapter 에 맡 겼 고 spring security 에서 많은 설정 류 는 Security Configurer Adapter 를 계승 하여 이 루어 졌 다.구현 클래스 Security ConfigurerAdapter 의 원본 코드 를 보십시오.

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {

 private B securityBuilder;

 private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();

 @Override
 public void init(B builder) throws Exception {
 }

 @Override
 public void configure(B builder) throws Exception {
 }

 /**
 *   SecurityBuilder,            
 */
 public B and() {
 return getBuilder();
 }

 /**
 *    SecurityBuilder
 */
 protected final B getBuilder() {
 Assert.state(this.securityBuilder != null, "securityBuilder cannot be null");
 return this.securityBuilder;
 }

 /**
 *          。       ObjectPostProcessor  
 * @return          
 */
 @SuppressWarnings("unchecked")
 protected <T> T postProcess(T object) {
 return (T) this.objectPostProcessor.postProcess(object);
 }

 public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
 this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
 }

 public void setBuilder(B builder) {
 this.securityBuilder = builder;
 }

 /**
 * ObjectPostProcessor     
 */
 private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
 private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
 @Override
 @SuppressWarnings({ "rawtypes", "unchecked" })
 public Object postProcess(Object object) {
  //        postProcess  
 for (ObjectPostProcessor opp : this.postProcessors) {
 Class<?> oppClass = opp.getClass();
 Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);
 if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
  object = opp.postProcess(object);
 }
 }
 return object;
 }
  // list           
 private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
 boolean result = this.postProcessors.add(objectPostProcessor);
 this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
 return result;
 }

 }
}

응...이 두 가지 방법 은 모두 공 실현 이 므 로 뒤의 하위 클래스 에 게 자신 이 다시 쓰 는 방법 을 맡 겨 야 한다.더 나 온 내용 은 Composite Object PostProcessor 를 초기 화하 고 이 를 바탕 으로 두 가지 방법 을 봉 했다.
Composite Object PostProcessor 는 Object PostProcessor 의 실현 이 고 Object PostProcessor 는 실제 적 으로 사후 처리 장치 입 니 다.
그 다음 에 addObject PostProcessor 방법 은 실제 list 에 백업 프로 세 서 를 추가 하고 정렬 하 는 것 입 니 다.그 다음 에 post Process 방법 에서 이 list 를 옮 겨 다 니 며 Object PostProcessor 의 일반적인 유형 과 전 달 된 매개 변수 유형 이 부자 관계 인지 판단 하고 post Process 방법 을 다시 호출 합 니 다.
이곳 은 왜 postprocess 를 한 번 더 호출 해 야 하 는 지 의 심 스 러 울 수 있 습 니 다.이것 은 재 귀적 인 것 이 아 닙 니까?Composite Object PostProcessor 류 는 private 입 니 다.즉,Security Configurer Adapter 내부 에서 만 사용 할 수 있 습 니 다.여기 서 postprocess 방법 을 다시 호출 하 는 것 은 다른 Object PostProcessor 의 실현 이 어야 합 니 다.
Object PostProcessor 는 모두 두 가지 실현 이 있 고,또 하 나 는 AutowireBean Factory Object PostProcessor 입 니 다.

final class AutowireBeanFactoryObjectPostProcessor
 implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {

 private final Log logger = LogFactory.getLog(getClass());

 private final AutowireCapableBeanFactory autowireBeanFactory;

 private final List<DisposableBean> disposableBeans = new ArrayList<>();

 private final List<SmartInitializingSingleton> smartSingletons = new ArrayList<>();

 AutowireBeanFactoryObjectPostProcessor(AutowireCapableBeanFactory autowireBeanFactory) {
 Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
 this.autowireBeanFactory = autowireBeanFactory;
 }

 @Override
 @SuppressWarnings("unchecked")
 public <T> T postProcess(T object) {
 if (object == null) {
 return null;
 }
 T result = null;
 try {
 result = (T) this.autowireBeanFactory.initializeBean(object, object.toString());
 }
 catch (RuntimeException ex) {
 Class<?> type = object.getClass();
 throw new RuntimeException("Could not postProcess " + object + " of type " + type, ex);
 }
 this.autowireBeanFactory.autowireBean(object);
 if (result instanceof DisposableBean) {
 this.disposableBeans.add((DisposableBean) result);
 }
 if (result instanceof SmartInitializingSingleton) {
 this.smartSingletons.add((SmartInitializingSingleton) result);
 }
 return result;
 }

 @Override
 public void afterSingletonsInstantiated() {
 for (SmartInitializingSingleton singleton : this.smartSingletons) {
 singleton.afterSingletonsInstantiated();
 }
 }

 @Override
 public void destroy() {
 for (DisposableBean disposable : this.disposableBeans) {
 try {
 disposable.destroy();
 }
 catch (Exception ex) {
 this.logger.error(ex);
 }
 }
 }
}

이 안 은 주로 autowireBeanFactory 를 통 해 대상 을 용기 에 주입 하 는데 security 에 서 는 많은 대상 이 new 로 나 옵 니 다.이 new 가 나 온 대상 은 용기 와 아무런 관련 이 없고 관리 하기 도 불편 하기 때문에 AutowireBeanFactory ObjectPostProcessor 를 통 해 대상 의 주입 을 완성 합 니 다.
즉,Security Configurer Adapter 에서 정 의 된 이 두 가지 방법 은 대상 을 spring 용기 에 넣 어 관리 하기 편리 하 다 는 것 이다.
AbstractConfigured Security Builder 분석
Security Configurer Adapter 의 내용 이 이렇게 많 습 니 다.AbstractHttp Configurer 를 계속 내 려 다 보 세 요.

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
 extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
 @SuppressWarnings("unchecked")
 public B disable() {
 getBuilder().removeConfigurer(getClass());
 return getBuilder();
 }

 @SuppressWarnings("unchecked")
 public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
 addObjectPostProcessor(objectPostProcessor);
 return (T) this;
 }
}
코드 가 적 습 니 다.두 번 째 방법 은 Security Configurer Adapter 를 호출 하 는 방법 입 니 다.여 기 는 첫 번 째 disable 방법 을 보 았 습 니 다.설정 류 에서 이미 사용 되 었 습 니 다.csrf 를 사용 하지 않 을 때 csrf().disable()을 호출 했 습 니 다.바로 이 방법 을 통 해 csrf 설정 을 제거 하 였 습 니 다.
disable 방법 을 계속 보 는 것 은 AbstractConfigured Security Builder 의 removeConfigurer 방법 을 호출 한 것 입 니 다.사실은 LinkedHashMap 의 요 소 를 제거 하 는 것 입 니 다.

private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
 List<C> configs = (List<C>) this.configurers.remove(clazz);
 if (configs == null) {
 return new ArrayList<>();
 }
 return new ArrayList<>(configs);
 }
제거 하 는 방법 이 있 으 니 추가 하 는 방법 이 있 을 것 이다.

private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
 Assert.notNull(configurer, "configurer cannot be null");
 Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
  .getClass();
 synchronized (this.configurers) {
  if (this.buildState.isConfigured()) {
   throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
  }
  List<SecurityConfigurer<O, B>> configs = null;
  if (this.allowConfigurersOfSameType) {
   configs = this.configurers.get(clazz);
  }
  configs = (configs != null) ? configs : new ArrayList<>(1);
  configs.add(configurer);
  this.configurers.put(clazz, configs);
  if (this.buildState.isInitializing()) {
   this.configurersAddedInInitializing.add(configurer);
  }
 }
}
사용자 정의 문자 로그 인 을 할 때 설정 클래스 에 사용자 정의 설정 을 추가 합 니 다:.apply(sms Authentication Security Config).이 apply 방법 은 실제 적 으로 위의 방법 을 호출 하여 설정 을 추가 합 니 다.
설정 이 이 용기 에 추가 되 었 으 니 언제 꺼 내 서 사용 합 니까?

private Collection<SecurityConfigurer<O, B>> getConfigurers() {
 List<SecurityConfigurer<O, B>> result = new ArrayList<>();
 for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
 result.addAll(configs);
 }
 return result;
}
//    configurer      
private void init() throws Exception {
 Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
 for (SecurityConfigurer<O, B> configurer : configurers) {
  configurer.init((B) this);
 }
 for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
  configurer.init((B) this);
 }
}
//      configure,    configure  
private void configure() throws Exception {
 // LinkedHashMap    configurer
 Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
 for (SecurityConfigurer<O, B> configurer : configurers) {
  configurer.configure((B) this);
 }
}
init 와 configure 방법 에서 설정 류 의 configure 방법 을 호출 했 습 니 다.여기 서 전체 프로 세 스 가 통 했 습 니 다.
Google 은 일반적으로 사용자 정의 로그 인 을 통 해 이 configure 방법 을 실현 합 니 다.이 방법 에서 filter 를 초기 화하 고 필터 체인 에 추가 합 니 다.
이러한 init 와 configure 방법 은 실제 적 으로 Security Builder 를 호출 하 는 build 방법 으로 호출 되 었 습 니 다.구체 적 인 코드 링크 는 말 하지 않 고 관심 있 는 것 은 직접 보 셔 도 됩 니 다.
마지막 으로 AbstractConfigured Security Builder 의 모든 코드 를 붙 입 니 다(간소화 되 었 습 니 다).

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
 extends AbstractSecurityBuilder<O> {
 private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
 private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
 private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
 private final boolean allowConfigurersOfSameType;
 private ObjectPostProcessor<Object> objectPostProcessor;

 @SuppressWarnings("unchecked")
 public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
 configurer.addObjectPostProcessor(this.objectPostProcessor);
 configurer.setBuilder((B) this);
 add(configurer);
 return configurer;
 }

 public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
 add(configurer);
 return configurer;
 }

 @SuppressWarnings("unchecked")
 public <C> void setSharedObject(Class<C> sharedType, C object) {
 this.sharedObjects.put(sharedType, object);
 }

 @SuppressWarnings("unchecked")
 public <C> C getSharedObject(Class<C> sharedType) {
 return (C) this.sharedObjects.get(sharedType);
 }

 /**
 * Gets the shared objects
 * @return the shared Objects
 */
 public Map<Class<?>, Object> getSharedObjects() {
 return Collections.unmodifiableMap(this.sharedObjects);
 }
 
 @SuppressWarnings("unchecked")
 private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
 Assert.notNull(configurer, "configurer cannot be null");
 Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
 .getClass();
 synchronized (this.configurers) {
 if (this.buildState.isConfigured()) {
 throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
 }
 List<SecurityConfigurer<O, B>> configs = null;
 if (this.allowConfigurersOfSameType) {
 configs = this.configurers.get(clazz);
 }
 configs = (configs != null) ? configs : new ArrayList<>(1);
 configs.add(configurer);
 this.configurers.put(clazz, configs);
 if (this.buildState.isInitializing()) {
 this.configurersAddedInInitializing.add(configurer);
 }
 }
 }


 /**
 *   class name        
 */
 @SuppressWarnings("unchecked")
 public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
 List<C> configs = (List<C>) this.configurers.remove(clazz);
 if (configs == null) {
 return new ArrayList<>();
 }
 return new ArrayList<>(configs);
 }

 /**
 *   class name        
 */
 @SuppressWarnings("unchecked")
 public <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
 List<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz);
 if (configs == null) {
 return null;
 }
 Assert.state(configs.size() == 1,
 () -> "Only one configurer expected for type " + clazz + ", but got " + configs);
 return (C) configs.get(0);
 }

 @SuppressWarnings("unchecked")
 public B objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
 Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
 this.objectPostProcessor = objectPostProcessor;
 return (B) this;
 }

 protected <P> P postProcess(P object) {
 return this.objectPostProcessor.postProcess(object);
 }

 //    configurer      
 private void init() throws Exception {
 Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
 for (SecurityConfigurer<O, B> configurer : configurers) {
 configurer.init((B) this);
 }
 for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
 configurer.init((B) this);
 }
 }
 //      configure,    configure  
 private void configure() throws Exception {
  // LinkedHashMap    configurer
 Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
 for (SecurityConfigurer<O, B> configurer : configurers) {
 configurer.configure((B) this);
 }
 }
 //       configure  
 protected final O doBuild() throws Exception {
 synchronized (this.configurers) {
 this.buildState = BuildState.INITIALIZING;
 beforeInit();
 init();
 this.buildState = BuildState.CONFIGURING;
 beforeConfigure();
 configure();
 this.buildState = BuildState.BUILDING;
 O result = performBuild();
 this.buildState = BuildState.BUILT;
 return result;
 }
 }
}

Spring Security 사용자 정의 문자 로그 인 인증 의 실현 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 SpringSecurity 문자 로그 인 인증 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기