티스토리 뷰
Spring Advanced : Web MVC + Security + Hibernate with Java Config - 유저 인증 및 등록 구현하기 5
Korean Eagle 2020. 5. 20. 01:291. 이 포스트는 Spring : Web MVC + Security + JDBC 시리즈에 연장이다. xml파일 설정은 사용하지 않는다.
1-1 하려는 것은 우선 in-memory로 인증을 구현한다.
1-2 Database를 생성하고 hibernate로 유저 등록을 구현한다.
1-3 가입정보에 대한 Validation처리를 작성한다. Customer Validatior로 구현한다. 이 포스트 내용
1-4 In-memory가 아닌 DaoAuthenticationProvider로 hibernate를 사용한 Spring security 인증처리로 변경
1-5 위에 것을 한번에 다 할려면 난이도가 헬이라서 이렇게 분리해서 한다.
2. 이 포스트에서는 비밀번호와 비밀번호 확인이 일치하는지에 대한 검증하는 Custom Validator를 작성한다.
3. 먼저 annotation을 만든다.
3-1 @FieldMatch annotation 클래스를 설정한다. 검증 클래스는 FieldMathValidator로 지정한다.
3-2 설정 타겟은 UserDTO클래스이기 때문에 Type으로 설정한다.
3-3 실행은 실시간이기 때문에 @Retention을 실시간으로 설정해야 한다.
3-4 비교할 속성을 지정할 first(), second()를 정의한다.
3-5 나머지는 bolierplate 코드이다.
3-6 아래 코드에서 내부의 @interface List는 신경 쓸 필요 없다. 이것은 2쌍 이상을 체크할 때 필요하다.
package pe.pilseong.custom_registration.validation;
...
@Constraint(validatedBy = FieldMatchValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldMatch {
// target fields name
String first();
String second();
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@interface List {
FieldMatch[] value();
}
}
4. 검증을 위한 FieldMatchValidator클래스를 생성한다.
4-1 firstFieldName, secondFieldName은 비교할 비밀번호의 Entity속성이 들어온다.
4-1-0 이 속성들은 initialize() 메소드를 overrride 해서 지정한다. 이 메소드는 인터페이스에서 기본 구현되어 있다.
4-1-1 initialize()의 파라메터는 검증할 annotation클래스이고 여기서 선언에서 기술한 값을 얻어올 수 있다.
4-1-1-1 여기서의 값은 검증할 두개의 password 입력 속성의 이름이다.
4-1-2-2 이 경우는 password, matchingPassword가 저장된다. message도 받아온다.
4-2 annotation이 클래스에 지정된 경우 검증로직에서는 isValid의 첫번째 인자는 지정된 클래스 UserDTO가 된다.
4-3 이 클래스에서 입력된 두 값은 UserDTO를 BeanWrapperImple에 연결하여 얻어올 수 있다.
4-3-1 BeanWrapperImpl로 객체 정보를 읽어 실제 각 속성에 입력된 비밀번호 문자열이 반환된다.
4-4 검증을 마친 후 실제 검증 결과가 false가 된 경우
4-4-1 클래스에 지정된 annotation의 경우는 어디에 에러를 보여줄지 메시지가 어떻게 될지를 설정해야 한다.
4-4-2 이 설정을 isValid의 두번째 검증context 객체로 처리할 수 있다.
4-4-3 검증 context로 에러메시지를 설정하는 방법은
4-4-3-1 context의 default 오류 구문 사용을 disable하고 우리가 원하는 구문을 넣어야 한다.
4-4-3-2 annotation에서 지정한 message를 가지고 오류구문을 생성하고
4-4-3-3 어디에 붙일지를 addPropertyNode로 지정한 후
4-4-3-4 context에 새로 생성한 오류구문을 붙인다.
4-4-3-5 주의 해야 할 점은 addPropertyNode에서 지정하는 방법은 누적이라는 점이다.
4-4-3-5-1 addPropertyNode를 두 번 사용해서 password, matchingPassword 각각 지정한다고 해서
4-4-3-5-2 두 개의 에러메시지가 생기는 게 아니다. 속성 변수를 password.matchingPassword로 찾을 뿐이다.
4-4-3-5-3 다시 말하면 메시지는 하나 만 생기니 원하는 속성 한 곳만 지정한다.
package pe.pilseong.custom_registration.validation;
...
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
private String firstFieldName = "";
private String secondFieldName = "";
private String message = "";
@Override
public void initialize(FieldMatch constraintAnnotation) {
this.firstFieldName = constraintAnnotation.first();
this.secondFieldName = constraintAnnotation.second();
this.message = constraintAnnotation.message();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
boolean valid = true;
if (value instanceof UserDTO ) {
System.out.println("I love it");
}
final Object firstObj = new BeanWrapperImpl(value).getPropertyValue(firstFieldName);
final Object secondObj = new BeanWrapperImpl(value).getPropertyValue(secondFieldName);
// 둘 다 null이면 에러메시지를 보이지 않음, 단지 @NotNull에 걸린다.
// null이 아니라면 둘이 같으면 메시지를 보이지 않음
valid = firstObj == null && secondObj == null
|| firstObj != null && firstObj.equals(secondObj);
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(secondFieldName)
.addConstraintViolation();
}
return valid;
}
5. 이제 실제 UserDTO에 적용해 본다.
5-1 아래를 보면 두개의 @FieldMatch가 지정되어 있다. 둘 다 동일한 결과를 보여 준다.
5-2 첫번째는 comment되어 있는데 이렇게 해도 되지만 클래스에 여러 쌍을 테스트할 필요가 있을 때 유용하다.
package pe.pilseong.custom_registration.user;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
import pe.pilseong.custom_registration.validation.FieldMatch;
import pe.pilseong.custom_registration.validation.ValidEmail;
@Data
//@FieldMatch.List({@FieldMatch(first = "password", second = "matchingPassword", message = "Two Password fields must match")})
@FieldMatch(first = "password", second = "matchingPassword", message = "Two Password fields must match")
public class UserDTO {
@NotNull(message = "is required")
@Size(min = 4, max = 30, message = "Length ranges from 4 to 30")
private String userName;
@NotNull(message = "is required")
@Size(min = 4, max = 20, message = "Length ranges from 4 to 20")
private String password;
@NotNull(message = "is required")
@Size(min = 4, max = 20, message = "Length ranges from 4 to 20")
private String matchingPassword;
@NotNull(message = "is required")
@Size(min = 3, max = 30, message = "Length ranges from 3 to 30")
private String firstName;
@NotNull(message = "is required")
@Size(max = 30, message = "Maximum is 30")
private String lastName;
@NotNull(message = "is required")
@Size(min = 5, max = 50, message = "Length ranges from 5 to 50")
@ValidEmail(message = "Invliad Email")
private String email;
}
6. 결과 화면
7. 이번 포스트에서 한 내용은
7-1 패스워드 확인 검증을 Custom Validator을 작성하여 수행하였다.
7-2 이렇게 하면 내부 로직을 단순하게 가져갈 수 있다.
8 custom validator에 관련된 자세한 정보는 아래 링크를 참고한다.
'Spring > Spring Advanced' 카테고리의 다른 글
- Total
- Today
- Yesterday
- 도커 개발환경 참고
- AWS ARN 구조
- Immuability에 관한 설명
- 자바스크립트 멀티 비동기 함수 호출 참고
- WSDL 참고
- SOAP 컨슈머 참고
- MySql dump 사용법
- AWS Lambda with Addon
- NFC 드라이버 linux 설치
- electron IPC
- mifare classic 강의
- go module 관련 상세한 정보
- C 메모리 찍어보기
- C++ Addon 마이그레이션
- JAX WS Header 관련 stackoverflow
- SOAP Custom Header 설정 참고
- SOAP Custom Header
- SOAP BindingProvider
- dispatcher 사용하여 설정
- vagrant kvm으로 사용하기
- git fork, pull request to the …
- vagrant libvirt bridge network
- python, js의 async, await의 차이
- go JSON struct 생성
- Netflix Kinesis 활용 분석
- docker credential problem
- private subnet에서 outbound IP 확…
- 안드로이드 coroutine
- kotlin with, apply, also 등
- 안드로이드 초기로딩이 안되는 경우
- navigation 데이터 보내기
- 레이스 컨디션 navController
- raylib