SpringSceurity 문자 인증 코드 로그 인 실현

1.문자 로그 인 검증 메커니즘 원리 분석
문자 인증 코드 의 로그 인 체 제 를 이해 하기 전에 저 희 는 먼저 사용자 계 정 비밀번호 로그 인 체제 가 어떻게 되 는 지 알 아야 합 니 다.Spring Security 가 사용자 이름과 비밀번호 로그 인 방식 을 어떻게 검증 하 는 지 간략하게 분석 해 보 겠 습 니 다.
분석 이 끝 난 뒤 문자 로그 인 검증 방식 을 스프링 시 큐 리 티 에 통합 하 는 방법 을 함께 고민해 보 자.
1.계 정 비밀번호 로그 인 절차
일반 계 정 비밀번호 로그 인 에는 도형 인증 코드 와 내 기능 이 첨부 되 어 있 는데 그 대략적인 절 차 는 이렇다.
1.사용자 가 사용자 이름,계 정,이미지 인증 코드 를 입력 한 후에 클릭 하여 로그 인 합 니 다.그러면 spring Sceurity 에 대해 서 는 먼저 문자 인증 코드 Filter 에 들 어 갑 니 다.설정 할 때 설정 하기 때 문 입 니 다.
UsernamePassword AuthenticationFilter 에 앞서 현재 인증 코드 의 정 보 를 session 에 존재 하 는 이미지 인증 코드 와 검증 합 니 다.
2.문자 인증 코드 통과 후 UsernamePassword AuthenticationFilter 에 들 어가 입력 한 사용자 이름과 비밀번호 정보 에 따라 일시 적 으로 인증 권 이 없 는
 UsernamePassword AuthenticationToken,그리고 UsernamePassword AuthenticationToken 을 AuthenticationManager 에 맡 깁 니 다.
3.AuthenticationManager 자체 가 검증 처 리 를 하지 않 습 니 다.그 는 for-each 를 통 해 현재 로그 인 방식 에 맞 는 AuthenticationProvider 를 찾 아 검증 처 리 를 합 니 다.
사용자 이름 비밀번호 로그 인 방식 에 대해 이 Provider 가 바로 Dao AuthenticationProvider 입 니 다.
4.이 Provider 에서 일련의 검증 처 리 를 하고 검증 이 통과 되면 인증 권 을 추가 한 UsernamePassword AuthenticationToken 을 재 구성 하고 이 를
 token 은 UsernamePassword AuthenticationFilter 로 전 달 됩 니 다.
5.이 Filter 의 부모 클래스 Abstract Authentication Processing Filter 에 서 는 이전 검증 결과 에 따라 successHandler 또는 failure Handler 로 이동 합 니 다.
흐름 도

2.문자 인증 코드 로그 인 절차
문자 로그 인 방식 이 Spring Security 에 통합 되 지 않 았 기 때문에 문자 로그 인 논 리 를 개발 하여 Spring Security 에 통합 시 켜 야 하 는 경우 가 많 습 니 다.그러면 여기 서 우 리 는 계 정 을 모방 합 니 다.
비밀번호 로그 인하 여 문자 인증 코드 로그 인 을 실현 합 니 다.
1.사용자 이름 비밀번호 로그 인 에 UsernamePassword AuthenticationFilter 가 있 습 니 다.우 리 는 Sms AuthenticationFilter 를 만 들 고 코드 를 붙 여 고 칩 니 다.
2.사용자 이름 비밀번호 로그 인 은 UsernamePassword AuthenticationToken 이 필요 합 니 다.우 리 는 Sms AuthenticationToken 을 만 들 고 코드 를 붙 여 고 쳐 야 합 니 다.
3.사용자 이름 비밀번호 로그 인 은 Dao AuthenticationProvider 가 필요 합 니 다.이 를 모방 하 는 것 도 implenments AuthenticationProvider 입 니 다.Sms AuthenticationProvider 라 고 합 니 다.

이 그림 은 인터넷 에서 찾 은 것 이 므 로,자신 은 그 리 려 고 하지 않 는 다.
우리 스스로 위의 세 가지 유형 을 만 든 후에 실현 하고 자 하 는 효 과 는 위의 그림 과 같다.우리 가 문자 인증 코드 로 로그 인 할 때:
1.먼저 Sms AuthenticationFilter 를 거 쳐 인증 권 이 없 는 Sms AuthenticationToken 을 구성 한 다음 AuthenticationManager 에 맡 깁 니 다.
2.AuthenticationManager 는 for-each 를 통 해 적당 한 provider 를 선택 하여 처리 합 니 다.물론 이 provider 가 Sms AuthenticationProvider 라면 좋 겠 습 니 다.
3.검증 이 통과 되면 인증 권 이 있 는 Sms AuthenticationToken 을 재 구성 하고 Sms AuthenticationFilter 에 되 돌려 줍 니 다.
filter 는 이전 검증 결과 에 따라 성공 하거나 실패 한 처리 논리 로 넘 어 갑 니 다.
코드 구현
1、SmsAuthenticationToken
먼저 Sms AuthenticationToken 을 작 성 했 습 니 다.여 기 는 UsernamePassword AuthenticationToken 소스 코드 를 직접 참고 하여 직접 붙 여 고 칩 니 다. principal 은 원래 사용자 이름 을 대표 하 는데 여 기 는 보류 하고 핸드폰 번호 만 대표 합 니 다.
credentials 원래 코드 비밀번호,문자 로그 인 이 사용 되 지 않 아 바로 삭제 합 니 다.
Sms Code Authentication Token()두 가지 구조 방법 중 하 나 는 구조 가 감 권 이 없 는 것 이 고 하 나 는 구조 가 감 권 이 있 는 것 이다.
나머지 몇 가지 방법 으로 무용 속성 을 제거 하면 됩 니 다.

public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {

 private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

 /**
 *   UsernamePasswordAuthenticationToken             ,
 *              
 */
 private final Object principal;

 /**
 *           SmsCodeAuthenticationToken
 */
 public SmsCodeAuthenticationToken(Object principal) {
 super(null);
 this.principal = principal;
 setAuthenticated(false);
 }

 /**
 *         SmsCodeAuthenticationToken
 */
 public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
 super(authorities);
 this.principal = principal;
 // must use super, as we override
 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();
 }
}
2、SmsAuthenticationFilter
그리고 Sms AuthenticationFilter 를 작성 하고 UsernamePassword AuthenticationFilter 의 원본 코드 를 참고 하여 직접 붙 여 고치 세 요. 원래 정적 필드 는 username 과 password 가 있 습 니 다.모두 제거 하고 우리 의 핸드폰 번호 필드 로 바 꿉 니 다.
Smscode AuthenticationFilter()에서 이 filter 의 차단 Url 을 지 정 했 습 니 다.저 는 post /sms/login 으로 지 정 했 습 니 다.
남 은 방법 은 무효 한 것 을 삭제 하고 고치 면 된다.

public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 /**
 * form          name
 */
 public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";

 private String mobileParameter = "mobile";
 /**
 *     POST   
 */
 private boolean postOnly = true;

 public SmsCodeAuthenticationFilter() {
 //         /sms/login     post
 super(new AntPathRequestMatcher("/sms/login", "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);

 // Allow subclasses to set the "details" property
 setDetails(request, authRequest);

 return this.getAuthenticationManager().authenticate(authRequest);
 }

 protected String obtainMobile(HttpServletRequest request) {
 return request.getParameter(mobileParameter);
 }

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

 public String getMobileParameter() {
 return mobileParameter;
 }

 public void setMobileParameter(String mobileParameter) {
 Assert.hasText(mobileParameter, "Mobile parameter must not be empty or null");
 this.mobileParameter = mobileParameter;
 }

 public void setPostOnly(boolean postOnly) {
 this.postOnly = postOnly;
 }
}
3、SmsAuthenticationProvider
이 방법 은 비교적 중요 하 다.이 방법 은 먼저 문자 인증 코드 를 사용 하여 로그 인 할 때 AuthenticationManager 에 의 해 선택 되 고 그 다음 에 이 클래스 에서 검증 논 리 를 처리 해 야 한다. AuthenticationProvider 인 터 페 이 스 를 실현 하고 authenticate()와 supports()방법 을 실현 합 니 다.

public class SmsCodeAuthenticationProvider implements AuthenticationProvider {

 private UserDetailsService userDetailsService;

 /**
 *   session   
 */
 private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

 String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_SMS";

 @Override
 public Authentication authenticate(Authentication authentication) throws AuthenticationException {
 SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;

 String mobile = (String) authenticationToken.getPrincipal();

 checkSmsCode(mobile);

 UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
 //        ,     new         authenticationResult   
 SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
 authenticationResult.setDetails(authenticationToken.getDetails());

 return authenticationResult;
 }

 private void checkSmsCode(String mobile) {
 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
 //  session        
 SmsCode smsCodeInSession = (SmsCode) sessionStrategy.getAttribute(new ServletWebRequest(request), SESSION_KEY_PREFIX);
 String inputCode = request.getParameter("smsCode");
 if(smsCodeInSession == null) {
  throw new BadCredentialsException("         ");
 }

 String mobileSsion = smsCodeInSession.getMobile();
 if(!Objects.equals(mobile,mobileSsion)) {
  throw new BadCredentialsException("       ");
 }

 String codeSsion = smsCodeInSession.getCode();
 if(!Objects.equals(codeSsion,inputCode)) {
  throw new BadCredentialsException("     ");
 }
 }

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

 public UserDetailsService getUserDetailsService() {
 return userDetailsService;
 }

 public void setUserDetailsService(UserDetailsService userDetailsService) {
 this.userDetailsService = userDetailsService;
 }
}
4、SmsCodeAuthenticationSecurityConfig
차단 기 를 사용자 정의 한 이상 설정 에서 변경 할 수 있 습 니 다.

@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
 @Autowired
 private SmsUserService smsUserService;
 @Autowired
 private AuthenctiationSuccessHandler authenctiationSuccessHandler;
 @Autowired
 private AuthenctiationFailHandler authenctiationFailHandler;

 @Override
 public void configure(HttpSecurity http) {
 SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
 smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
 smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(authenctiationSuccessHandler);
 smsCodeAuthenticationFilter.setAuthenticationFailureHandler(authenctiationFailHandler);

 SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
 //                           
 smsCodeAuthenticationProvider.setUserDetailsService(smsUserService);

 http.authenticationProvider(smsCodeAuthenticationProvider)
  .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 }
}
5、SmsUserService
사용자 이름,비밀번호 로그 인 은 최종 적 으로 사용자 이름 을 통 해 사용자 정 보 를 조회 하 는 것 이 고,모 바 일 인증 코드 로그 인 은 모 바 일 로그 인 을 통 해 이 루어 지기 때문에 자신 이 SmsUserService 를 하나 더 실현 해 야 합 니 다.

@Service
@Slf4j
public class SmsUserService implements UserDetailsService {

 @Autowired
 private UserMapper userMapper;

 @Autowired
 private RolesUserMapper rolesUserMapper;

 @Autowired
 private RolesMapper rolesMapper;

 /**
 *        
 */
 @Override
 public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
 log.info("       ,     = {}",mobile);
 //TODO                   sql,       user    ,   mobile  ,           
 //TODO                    (    )
 User user = userMapper.findOneByUsername("  ");
 if (user == null) {
  throw new UsernameNotFoundException("        ");
 }
 //                         
 List<RolesUser> userList = rolesUserMapper.findAllByUid(user.getId());
 if (CollectionUtils.isEmpty(userList)) {
  return user;
 }
 //    ID  
 List<Integer> ridList = userList.stream().map(RolesUser::getRid).collect(Collectors.toList());
 List<Roles> rolesList = rolesMapper.findByIdIn(ridList);
 //        
 user.setRoles(rolesList);
 return user;
 }
}
6.총화
여기까지 오 면 생각 이 뚜렷 해 집 니 다.제 가 정리 하 겠 습 니 다.
1.먼저 인증 을 받 을 때 부터 현재 인증 코드 정 보 를 session 에 저 장 했 습 니 다.이 정 보 는 인증 코드 와 핸드폰 번 호 를 포함 합 니 다.
2.사용자 가 인증 로그 인 을 입력 합 니 다.여 기 는 Sms AuthenticationFilter 에 직접 쓰 여 있 습 니 다.먼저 인증 코드,핸드폰 번호 가 정확 한 지 확인 한 다음 에 사용자 정 보 를 조회 합 니 다.저희 도 아 이 디 비밀번호 로 뜯 어서 로그 인 할 수 있어 요.
필 터 는 인증 코드 와 핸드폰 번호 가 정확 한 지 검증 하고 인증 코드 를 걸 어서 필 터 를 로그 인 합 니 다.
3.Sms Authentication Filter 프로 세 스에 서도 키 와 관련 된 단 계 는 사용자 이름 비밀번호 로그 인 은 사용자 정의 UserService 를 통 해 UserDetails 서 비 스 를 실현 한 후 사용자 이름 을 통 해 사용자 이름 정 보 를 조회 하 는 것 입 니 다.
휴대 전화 번 호 를 통 해 사용자 정 보 를 조회 하기 때문에 SmsUserService 를 사용자 정의 하여 UserDetails Service 를 실현 한 후.
테스트
1.인증번호 가 져 오기

인증 코드 를 받 은 휴대 전화 번 호 는 15612345678 이다.제3자 문자 SDK 를 받 지 않 고 백 스테이지 에서 만 출력 했 기 때문이다.
핸드폰 번호:15612345678 의 사용자 에 게 인증 번 호 를 보 냅 니 다:254792
2.로그 인
1)인증번호 입력 이 정확 하지 않 음

로그 인 에 실 패 했 습 니 다.마찬가지 로 핸드폰 번호 입력 이 틀 리 면 로그 인 에 실 패 했 습 니 다.
2)로그 인 성공

휴대 전화 번호 와 문자 인증 번호 가 모두 정확 한 상황 에서 로그 인 에 성공 했다.
레 퍼 런 스
1.Spring Security 기술 창고 개발 기업 급 인증 및 권한 수여(JoJo)
2、 SpringSceurity 문자 인증 코드 기능 구현 예시 코드
SpringSceurity 의 문자 인증 코드 로그 인 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.SpringSceurity 문자 인증 코드 로그 인 내용 에 대해 서 는 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 읽 어 주시 기 바 랍 니 다.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기