vue + jpa project (15) - 로그인 및 백엔드 환경 초기 구성 본문

프로그램/Vue.js

vue + jpa project (15) - 로그인 및 백엔드 환경 초기 구성

반응형

로그인 관련한 테이블을 아래와 같이 구성하려고 한다. 

우선 사용자 마스터와 권한관리에 대해서 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 Related Articles

MORE

Comments