본문 바로가기

프로젝트/멋사 개인 프로젝트 (mutsa-SNS)

[13] mutsa-SNS 6일차 - (2) Admin 권한 부여 기능 추가

생각보다 test code의 작성이 빨리 끝나 도전과제인 admin 권한 부여에 도전해 보았다.

  • ADMIN 회원으로 등급업하는 기능
  • 초기 ADMIN 회원은 하나가 존재하고 ADMIN 회원은 일반회원의 권한을 ADMIN으로 승격시킬 수 있다.
  • ADMIN 회원이 로그인 시 자신이 쓴 글이 아닌 글에 대해 수정, 삭제 가능

 

밑의 두가지 기능은 이미 진행하였기 때문에, USER 회원을 ADMIN으로 등급업 하는 기능을 추가하였다.

등급업이 가능한 유저는 ADMIN 유저이다.

 

1. Configuration

SecurityConfig

@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
//WebSecurityConfigurerAdapter 는 이후 SpringBoot에서 잘 안쓰게 됨
public class SecurityConfig {

    private final UserService userService;

    @Value("${jwt.token.secret}")
    private String secretKey;

    ...

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .httpBasic().disable()
                .csrf().disable()
                .cors().and()
                .exceptionHandling()
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                .and()
                .authorizeRequests()
                .antMatchers(PERMIT_URL_ARRAY).permitAll()
                .antMatchers(HttpMethod.POST,"/api/**").authenticated() // post는 인가자만 허용
                .antMatchers(HttpMethod.DELETE,"/api/**").authenticated() // delete는 인가자만 허용
                .antMatchers(HttpMethod.PUT,"/api/**").authenticated() // put는 인가자만 허용 (글 수정)
                .antMatchers("/api/v1/users/**/role/change").authenticated() // role 수정
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt사용하는 경우 씀
                .and()
                .addFilterBefore(new JwtTokenFilter(userService, secretKey), UsernamePasswordAuthenticationFilter.class) //UserNamePasswordAuthenticationFilter적용하기 전에 JWTTokenFilter를 적용
                .build();
    }

}

역할을 수정하는 URL에는 일단 로그인 한 유저만 접근할 수 있도록 권한 부여를 해 주었다. .antMatchers("/api/v1/users/**/role/change").authenticated()

 

나중에 service에서 userRole을 검증하지 않고, filter에서 검증할 수 있는 방법이 있다면 찾아보고, 리팩토링 해야 할 듯 하다.

 

2. Service

UserService

@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder encoder;

   	...
    
    public UserDto changeUserRole(Integer userId, String adminUserName) {

        //admin userName이 존재하는가
        User adminUser = userRepository.findByUserName(adminUserName)
                .orElseThrow(()-> new AppException(ErrorCode.NOT_FOUNDED_USER_NAME, ""));

        //adminUser의 유저 권한이 ADMIN이 아니면 exception
        if(adminUser.getRole() != UserRole.ADMIN) {
            throw new AppException(ErrorCode.INVALID_PERMISSION ,"");
        }

        //권한 바꿀 user의 userName이 존재하는가
        User user = userRepository.findById(userId)
                .orElseThrow(()-> new AppException(ErrorCode.NOT_FOUNDED_USER_NAME, ""));

        //admin 권한 부여
        user.setRole(UserRole.ADMIN);

        User savedUser = userRepository.saveAndFlush(user);

        return UserDto.builder()
                .id(savedUser.getId())
                .userName(savedUser.getUserName())
                .role(savedUser.getRole())
                .build();

    }
}

유저의 역할을 바꿀 수 있는 로직을 추가하였다.

  • controller에서 넘겨받은 adminUserName (로그인 한 유저)를 통해 db에 검색하고, 없으면 exception 발생
  • adminUser (로그인 한 유저)의 userRole 이 ADMIN이 아니면 exception 발생
  • controller에서 넘겨받은 usrId (권한 수정할 유저)를 통해 db에 검색하고, 없으면 exception 발생
  • set method (@setter를 User에 추가)를 통해 권한 변경
  • controller에 UserDto return

 

3. Controller

UserController

@RestController
@RequestMapping("/api/v1/users/")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    ...

    //role 변경
    @GetMapping("/{userId}/role/change")
    public Response<UserRoleChangeResponse> roleChange(@PathVariable Integer userId, Authentication authentication) {
        String adminUserName = authentication.getName();
        UserDto userDto = userService.changeUserRole(userId, adminUserName);
        return Response.success(new UserRoleChangeResponse("ADMIN 부여 완료", userId));
    }

}

유저의 역할을 바꿀 수 있는 controller를 추가하였다. 변경을 원하는 userId를 URL에서 받는다.

  • 로그인 한 유저의 userName과 변경을 원하는 user의 id를 service에 넘겨준다.
  • service에서 로직이 실행되어 exception이 발생하지 않으면 넘겨준 UserDto의 id와 성공 메세지를 출력한다.

userRole을 Authentication에서 뽑아 낼 까 했지만, service에서 검증하는 것이 나을 것 같다는 생각이 들었다.

뽑아낸 userRole의 형태가 Collection에 들어가있고, stream으로 뽑아내서 비교하는 것이 번거롭다...

 

<Reference>

https://jason-moon.tistory.com/132

 

Spring Security, 현재 사용자 정보 가져오기.

보통의 Entity(JPA에서의..)는 등록사용자ID, 등록일자를 가지게 된다.package kr.co.xfilegolf.sale; import kr.co.xfilegolf.SecurityUtils; import lombok.Data; import javax.persistence.*; import java.time.LocalDate; import java.time.LocalDa

jason-moon.tistory.com

https://www.tabnine.com/code/java/methods/org.springframework.security.core.Authentication/getAuthorities

 

org.springframework.security.core.Authentication.getAuthorities java code examples | Tabnine

Authentication authentication) { return authentication.getAuthorities();

www.tabnine.com

 

개인 프로젝트의 중간 점검 기한이 다 되었다.

원래 UI까지 추가해 보고자 하였으나,

중간중간 생각보다 시간을 잡아 먹었던 과정들 (ci/cd 추가, jwt token filter exception 추가, test code 작성)이 꽤 많아 시간이 부족하여 시도하지 못하였다.

만약 최종 점검 전에 시간이 남는다면 시도해 볼 듯 하다... api만 출력되는 것은 보는 맛이 부족한 듯 하다...

 

또한, 코드 리팩토링 시간에 시간이 된다면, 위의 기능 (ADMIN 권한 부여)에 대한 test code도 추가해야 할 듯 하다.

제출 시간이 임박하여 웹에 직접 돌려 테스트밖에 하지 못하였다...

 

수업 시간에는 다소 따라치기 바빴던 코드들을 백지상태에서 직접 짜보고 에러들을 직접 경험해 보면서, 이제는 조금 자신감이 생긴 듯 하다.

갈 길이 멀었지만, 모르는 부분은 차근차근 공부해봐야겠다는 생각이 들었다.

 

<AWS EC2 배포 URL>

http://ec2-3-35-225-29.ap-northeast-2.compute.amazonaws.com:8080/swagger-ui/#/

 

<전체 Reference>

https://jason-moon.tistory.com/132

https://www.tabnine.com/code/java/methods/org.springframework.security.core.Authentication/getAuthorities