vue + jpa project (15) - 로그인 및 백엔드 환경 초기 구성 본문
vue + jpa project (15) - 로그인 및 백엔드 환경 초기 구성
- 2023. 11. 1. 16:02
로그인 관련한 테이블을 아래와 같이 구성하려고 한다.
우선 사용자 마스터와 권한관리에 대해서 1:N 구조로 가져가고 사용자 토큰도 단일 구조로 가져가려고 한다.
1. 위의 테이블에 대한 Entity와 Dto를 생성한다.
각각 entity패키지와 dto패키지에 각각 아래와 같이 생성한다.
// UserEntity.java
@JsonIgnoreProperties(value="hibernateLazyInitializer")
@EqualsAndHashCode(of="userNo")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name="user_master")
@Entity
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userNo;
@Column(length = 50, nullable = false)
private String userId;
@Column(length = 200, nullable = false)
private String userPw;
@Column(length = 100, nullable = false)
private String userName;
@Column(length = 5, nullable = false)
private String job;
@JsonFormat(pattern="yyyy-MM-dd HH:mm")
@CreationTimestamp
private LocalDateTime regDate;
@JsonFormat(pattern="yyyy-MM-dd HH:mm")
//@UpdateTimestamp
private LocalDateTime updDate;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name = "user_no")
private List<UserAuthEntity> authList = new ArrayList<UserAuthEntity>();
public void addAuth(UserAuthEntity auth) {
authList.add(auth);
}
public void clearAuthList() {
authList.clear();
}
}
// UserAuthEntity.java
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Builder
@Table(name="user_auth")
public class UserAuthEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userAuthNo;
@Column(name = "user_no")
private Long userNo;
@Column(length = 50)
private String auth;
@JsonFormat(pattern="yyyy-MM-dd HH:mm")
@CreationTimestamp
private LocalDateTime regDate;
@JsonFormat(pattern="yyyy-MM-dd HH:mm")
//@UpdateTimestamp
private LocalDateTime updDate;
}
// TokenEntity.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name="user_token")
@Entity
public class TokenEntity implements Serializable {
private static final long serialVersionUID = -4091430091078824820L;
@Id
@Column(name = "userNo")
private Long userNo;
@Column(name = "refreshToken", nullable = false, columnDefinition = "VARCHAR(500) CHARACTER SET UTF8")
private String refreshToken;
@Column(name = "ip", nullable = true, columnDefinition = "VARCHAR(20) CHARACTER SET UTF8")
private String ip;
@Column(name = "exprDate", nullable = false, columnDefinition = "datetime(6)")
private Date exprDate;
@CreationTimestamp
@Column(name = "regDate", nullable = false, columnDefinition = "datetime(6)")
private Date regDate;
}
@Column 을 보면 조금 다양하게 작성을 한 부분이 있다. 여러가지 방안 중에서 본인에게 편한 방법으로 작성하면 된다.
아래는 Dto 파일이다.
// UserDto.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserDto implements Serializable {
private static final long serialVersionUID = 2437315342927794834L;
private Long userNo;
private String userId;
private String userPw;
private String userName;
private String job;
private LocalDateTime regDate;
private LocalDateTime updDate;
}
// TokenDto.java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TokenDto implements Serializable {
private static final long serialVersionUID = 4522956936708833836L;
private Long userNo;
private String accessToken;
private String refreshToken;
private String ip;
private Date exprDate;
private Date regDate;
}
2. 사용자와 토큰에 대한 레파지토리 파일을 생성한다.
repository 패키지에 아래와 같이 생성한다.
// UserRepository.java
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByUserId(String userId);
}
findByUserId는 따로 조회시 필요해서 추가한다.
// TokenRepository.java
public interface TokenRepository extends JpaRepository<TokenEntity, Long> {
}
3. 사용자와 토큰에 대한 서비스 파일을 생성한다.
service 패키지에 아래와 같이 생성한다.
UserService의 경우에는 SpringSecurity 를 통해서 처리가 되어야 하는 부분이 있어서 UserDetailsService를
확장하여 구성한다. loadUserByUsername 파라미터가 username이지만 실제론 userId이다. 일단 기본대로 간다.
// UserService.java
@RequiredArgsConstructor
@EnableWebSecurity
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> authorities = new ArrayList<>();
UserEntity userEntity = userRepository.findByUserId(username)
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다."));
if (userEntity.getUserId().equals(username)) {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
return new User(userEntity.getUserId(), userEntity.getUserPw(), authorities);
}
/**
* 사용자 상세 조회
*/
public UserDto selectUserInfo(String userId) throws UsernameNotFoundException {
UserEntity entity = userRepository.findByUserId(userId)
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다."));
return entityToDto(entity);
}
/* 테이블 데이터 -> 화면쪽으로 */
private UserDto entityToDto(UserEntity entity) {
UserDto dto = UserDto.builder()
.userNo(entity.getUserNo())
.userId(entity.getUserId())
.userPw(entity.getUserPw())
.userName(entity.getUserName())
.job(entity.getJob())
.regDate(entity.getRegDate())
.updDate(entity.getUpdDate())
.build();
return dto;
}
}
// TokenService.java
@Slf4j
@RequiredArgsConstructor
@Service
public class TokenService {
private final TokenRepository tokenRepository;
/**
* 회원 JWT Token 발급 정보를 조회
*/
public TokenDto retrieveUserJWTTokenInfo(Long userNo) throws UsernameNotFoundException {
TokenEntity entity = tokenRepository.findById(userNo)
.orElseThrow(() -> new UsernameNotFoundException("토큰을 찾을 수 없습니다."));
return entityToDto(entity);
}
/**
* 회원 JWT Token 등록
*/
public TokenEntity create(TokenDto dto) throws Exception {
Optional<TokenEntity> entity = tokenRepository.findById(dto.getUserNo());
if(entity != null && !entity.isEmpty()) {
tokenRepository.delete(entity.get());
}
return tokenRepository.save(dtoToEntity(dto));
}
/* 입력건 -> 테이블로 */
private TokenEntity dtoToEntity(TokenDto dto) {
TokenEntity entity = TokenEntity.builder()
.userNo(dto.getUserNo())
.refreshToken(dto.getRefreshToken())
.ip(dto.getIp())
.exprDate(dto.getExprDate())
.regDate(new Date())
.build();
return entity;
}
/* 테이블 데이터 -> 화면쪽으로 */
private TokenDto entityToDto(TokenEntity entity) {
TokenDto dto = TokenDto.builder()
.userNo(entity.getUserNo())
.refreshToken(entity.getRefreshToken())
.ip(entity.getIp())
.exprDate(entity.getExprDate())
.regDate(entity.getRegDate())
.build();
return dto;
}
}
4. Spring Security 와 JWT를 사용하기 위한 설정을 진행한다.
build.gradle의 dependencies에 아래 내용을 추가하고, gradle refresh 를 수행한다.
// queryDsl 추가한 뒷부분에 추가
//* * * login * * *
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
// javax.xml.bind
implementation 'javax.xml.bind:jaxb-api:2.3.1'
//* * * login * * *
5. WebSecurity 에 대한 환경을 구성하기 위하여 WebSecurityConfig 파일을 아래와 같이 생성한다.
config 패키지 아래에 파일을 생성한다.
// WebSecurityConfig.java
@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class WebSecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration auth) throws Exception {
return auth.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable().authorizeRequests()
.anyRequest().permitAll()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.formLogin().disable();
return http.build();
}
}
6. 설정한 환경에 대하여 정상 작동하는지 확인을 해본다.
우선 아래와 같이 테스트 파일에 사용자 생성하는 메소드를 추가하고 JUnit 으로 실행해보자
// VueJpaProjectApplicationTests.java
@Autowired
UserRepository userRepository;
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
...중략
@DisplayName("테스트 사용자 생성하기")
@Test
void userCreateTest(){
String encPassword = passwordEncoder.encode("1111");
UserEntity userEntity = new UserEntity();
userEntity.setUserId("test_user1");
userEntity.setUserPw(encPassword);
userEntity.setUserName("테스트유저1");
userEntity.setJob("j01");
UserAuthEntity userAuthEntity = new UserAuthEntity();
userAuthEntity.setAuth("ROLE_MEMBER");
userEntity.addAuth(userAuthEntity);
UserEntity savedUser = userRepository.save(userEntity);
assertThat(userEntity.getUserId()).isEqualTo(savedUser.getUserId());
}
성공이 되었다면 테이블에 데이터가 생성되었는지 확인해보자.
잘 생성 되었다면 SpringSecurity 를 통해서 사용자 정보가 일치하는지를 확인하는 테스트를 해보자.
위의 메소드 뒤에 아래와 같이 추가하고 JUnit 으로 실행해보자.
// VueJpaProjectApplicationTests.java
...중략
@Autowired
UserService userService;
@Autowired
AuthenticationManager authenticationManager;
...중략
@DisplayName("유저정보 비밀번호 비교")
@Test
void userCheckTest(){
String userId = "test_user1";
String userPw = "1111";
UserDetails user = userService.loadUserByUsername(userId);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, userPw);
authenticationManager.authenticate(authenticationToken);
assertThat(authenticationToken.getCredentials()).isEqualTo(userPw);
System.out.println("저장된 패스워드 : " + authenticationToken.getCredentials());
}
위의 테스트도 무난히 통과를 했다면 기본적인 설정이 마무리가 된 셈이다.
다음 장에서는 JWT를 사용하여 토큰을 발급하고 처리하는 부분에 대해서 진행하겠다.
'프로그램 > Vue.js' 카테고리의 다른 글
vue + jpa project (17) - 화면이동 사전체크 및 로그아웃 처리 (0) | 2023.11.07 |
---|---|
vue + jpa project (16) - JWT 환경 구성 및 프론트 재수정 (0) | 2023.11.02 |
vue + jpa project (14) - vuex설정 및 로그인 화면 생성 (0) | 2023.10.31 |
vue + jpa project (13) - 게시판 첨부파일 처리(이미지) (0) | 2023.10.31 |
vue + jpa project (12) - 게시판 등록, 상세, 수정, 삭제 (0) | 2023.10.30 |
RECENT COMMENT