Spring Security 사용자 정의 문자 로그 인 인증 실현
31318 단어 SpringSecurity문자 로그 인 인증
위의 글 에서 사용자 의 로그 인 에 대해 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 문자 로그 인 인증 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[Spring] Spring Security - JWT강력한 사용자 인증 및 Access 제어 framework이다. 이는 Java 애플리케이션에 인증 및 권한 부여를 제공하는데 중점을 두었으며 다양한 필터를 사용하여 커스터마이징이 가능하다. '인증(Authentica...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.