Game login 구현하기 [1] api 권한 추가

최준호·2022년 4월 15일
0

game

목록 보기
5/14
post-thumbnail

출처

Spring Boot and Spring Security with JWT including Access and Refresh Tokens 해당 유튜브 영상을 참고하여 진행했다!

💬왜 수정?

기존의 코드는 token만을 발급하며 권한에 대한 정보도 없이 반환하는 형식이였다. 토큰으로만 접속하는 방법을 사용하기엔 보안적인 문제가 있으므로 이왕 할때 제대로 하자는 마인드로 access_token과 refresh_token을 구별하여 제대로 해보자.

🔨Entity 수정

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Table(name = "game_users")
public class GameUserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    @Column(name = "user_id", nullable = false, unique = true, length = 10)
    private String userId;
    @Column(nullable = false, length = 20)
    private String name;
    @Column(nullable = false, length = 150)
    private String pw;

    @ManyToMany(fetch = FetchType.EAGER)
    private Collection<GameRole> roles = new ArrayList<>();


    @CreationTimestamp
    private LocalDateTime createdAt = LocalDateTime.now();

    @Builder
    public GameUserEntity(@NonNull String userId,@NonNull String name,@NonNull String pw) {
        this.userId = userId;
        this.name = name;
        this.pw = pw;
    }
}

다음과 같이 권한 역할을 할 roles 를 추가하여주고 user 정보를 불러올때 함께 사용할 내용이므로 fetch 전략을 EAGER로 사용한다.

참고로 fetch 전략 EAGER는 아무데나 사용하면 나중에 피본다. 왜냐하면 User 테이블을 select 할때 무조건 GameRole 테이블을 함께 조회시키는 전략인데 이렇게 되면 나중에 쿼리의 속도가 엄청 느려지는 현상을 초래할 수 있다.

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class GameRole {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

GameRole의 코드

public interface GameRoleRepository extends CrudRepository<GameRole, Long> {
    GameRole findByName(String name);
}

나중에 사용할 repository도 미리 구현

다음과 같이 잘 구현되었다.
그리고 GAME_ROLE 테이블에 ROLE_ADMIN을 미리 등록해주어야한다.

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class GameUserServiceImpl implements GameUserService{
    private final BCryptPasswordEncoder passwordEncoder;
    private final GameUserRepository gameUserRepository;
    private final GameRoleRepository gameRoleRepository;

    @Override
    public ResponseGameUser join(RequestGameUser requestGameUser) {
        log.info("user join {}", requestGameUser.getName());
        //유효성 검사
        GameUserEntity user = gameUserRepository.findByUserId(requestGameUser.getUserId());
        if(user != null){
            throw new JoinException(UserCode.EXIST_USER);
        }

        GameUserEntity gameUserEntity = GameUserEntity.builder()
                .userId(requestGameUser.getUserId())
                .pw(passwordEncoder.encode(requestGameUser.getPw()))
                .name(requestGameUser.getName())
                .build();

        GameUserEntity save = gameUserRepository.save(gameUserEntity);
        addRoleToUser(save.getUserId(), "ROLE_ADMIN");
        ResponseGameUser responseGameUser = new ResponseGameUser(save.getUserId());

        return responseGameUser;
    }

    @Override
    public GameRole saveRole(GameRole role) {
        log.info("user save role {}",role.getName());
        return gameRoleRepository.save(role);
    }

    @Override
    public void addRoleToUser(String userId, String roleName) {
        log.info("user add user = {}, role = {}",userId, roleName);
        GameUserEntity user = gameUserRepository.findByUserId(userId);
        GameRole role = gameRoleRepository.findByName(roleName);
        user.getRoles().add(role);
    }

    @Override
    public GameUserVo getUserDetailByUserId(String userId) {
        log.info("getUserDetailByUserId {}",userId);
        GameUserEntity userEntity = gameUserRepository.findByUserId(userId);

        GameUserVo gameUserVo = GameUserVo.builder()
                .userId(userEntity.getUserId())
                .name(userEntity.getName())
                .pw(userEntity.getPw())
                .createdAt(userEntity.getCreatedAt())
                .build();

        return gameUserVo;
    }

    @Override
    public List<GameUserEntity> getAllUser() {
        return Lists.newArrayList(gameUserRepository.findAll());
    }

    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        GameUserEntity user = gameUserRepository.findByUserId(userId);
        if(user == null) throw new UsernameNotFoundException("user가 존재하지 않습니다.");

        return new User(user.getUserId(), user.getPw(), true, true, true, true, new ArrayList<>());
    }
}

기존 로직에 addRoleToUser() 메서드를 추가하여 우선은 ROLE_ADMIN만 추가되도록 해놨다. 나중에는 회원가입시 모두 ROLE_ADMIN을 가지고 ROLE_MANAGER나 기타 권한을 추가하는 화면도 만들어서 관리하면 좋을거 같아서 service에 정의해서 사용했다.

그리고 회원가입을 진행하면

다음과 같이 1번 계정에 1번의 role이 추가되었다는 것을 확인할 수 있는데 회원가입을 하나 더 해보자

2번 계정에도 1번 권한이 추가되어진 것을 확인할 수 있었고

모든 user의 정보를 불러왔을 때 다음과 같이 roles의 정보가 추가되어 가져올 수 있다는 것을 확인할 수 있다.

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class GameUserServiceImpl implements GameUserService{
    
    ...

    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        GameUserEntity user = gameUserRepository.findByUserId(userId);
        if(user == null) throw new UsernameNotFoundException("user가 존재하지 않습니다.");

        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        user.getRoles().forEach(role -> {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        });
        return new User(user.getUserId(), user.getPw(), true, true, true, true, authorities);
    }
}

그후 loadUserByUsername() 메서드에서 user의 정보를 만들 때 권한의 정보를 다음과 같이 넣어준다.

그러면

로그인 했을 때 다음과 같이 권한의 정보를 불러옴을 확인할 수 있다.

profile
코딩을 깔끔하게 하고 싶어하는 초보 개발자 (편하게 글을 쓰기위해 반말체를 사용하고 있습니다! 양해 부탁드려요!) 현재 KakaoVX 근무중입니다!

0개의 댓글