Dev JS Blog

스프링 시큐리티 - 2 (회원 등록) 본문

IT

스프링 시큐리티 - 2 (회원 등록)

Dev JS 2023. 3. 6. 23:59
728x90

2023.03.02 - [IT] - 스프링 시큐리티 - 1

 

스프링 시큐리티 - 1

의존성 추가 implementation("org.springframework.boot:spring-boot-starter-security:3.0.3") 스프링 시큐리티를 의존성을 추가하고 서버를 재기동하고 localhost:8080 를 열어보면 내가 만든적 없는 로그인 창이 등장하

allmana.tistory.com

 

URL 권한 주기

스프링 부트 3 버전에 시큐리티도 5.7 이상을 쓰다보니 예전에 했던 방식이랑 조금 달라졌다.

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable() // csrf 중지
                .headers().frameOptions().disable()// x-frame-options 중지
                .and()
                .authorizeRequests()
                .requestMatchers( // 원하는 url 패턴 추가 
                        "/h2-console/**", // h2-DB
                        "/api/member") // 회원가입을 위한 api
                .permitAll();
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // web security setting 
    }
}

 

H2 접근이 안돼?

security 사용하므로서.. h2-console에 접근도 안되게 된다. 그래서 겨우 찾아낸 결과..
1. crsf disable
2. header frameOptions disable
3. request 허용.

이전 버전과는 다르게 5.7 이상에서는 @Bean 선언해서 설정을 해주면된다.
나는 h2 DB를 사용하기 때문에 h2-console 경로도 추가해줬다.
그리고 api 서버로만 구성할것이기 때문에 로그인페이지나 로그아웃 페이지가 없이
회원 가입을 위한 api/member url만을 추가해줬다.

Member 테이블 생성

@Entity
@Getter
@NoArgsConstructor
public class Member{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String loginId;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String password;

    @OneToMany(mappedBy = "member")
    private List<MemberRole> roles = Lists.newArrayList();

    @Builder
    public Member(Long id, String loginId, String name, String password, List<MemberRole> roles) {
        this.id = id;
        this.loginId = loginId;
        this.name = name;
        this.password = password;
        this.roles = roles;
    }
}

Entity 에는 Setter를 쓰지 않는다.(자세한건 김영한님 강의 참고)
나머지는 회원에 대한 기본 정보이고 회원 권한은 MemberRole 이라는 엔티티를 하나 더 구성해서
회원들의 권한을 관리할 생각이다.

그 후 인터페이스인 UserDetails를 구현하면

@Entity
@Getter
@NoArgsConstructor
public class Member implements UserDetails {
    // 생략 ... 
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = Lists.newArrayList();
        // role을 넣어준다.
        roles.forEach(role->{
            authorities.add(new SimpleGrantedAuthority(role.getRoleType().toString()));
        });
        return authorities;
    }
    @Override
    public String getUsername() {
        return this.loginId; // 계정 아이디를 리턴해준다.
    }

    @Override
    public boolean isAccountNonExpired() { //true면 만료되지 않은 계정
        return true;
    }

    @Override
    public boolean isAccountNonLocked() { // true면 잠기지 않은 계정
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() { // true면 비밀번호가 만료되지 않은 계정
        return true;
    }

    @Override
    public boolean isEnabled() { // true면 활성화되어있는 계정
        return true;
    }
}

참고로 getPassword도 있는데 member 클래스에 password가 겹치고 getter가 있다보니 새로 뜨진 않았다.

MemberRole 생성

@Entity
@Getter
public class MemberRole {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;

    @Enumerated(EnumType.STRING)
    private RoleType roleType;
    
    @Builder
    public MemberRole(Long id, Member member, RoleType roleType) {
        this.id = id;
        this.member = member;
        this.roleType = roleType;
    }
}

Member에 OneToMany를 쓰긴 했지만 사실 ManyToOne 구조로만 만드는게 좋다.
하지만 멤버 권한만.. 특별히.. Memeber쪽에 OneToMany를 구성해주었고
MemberRole에는 Member와 조인될 컬럼을 지정해준다.

public enum RoleType {
    ROLE_USER,
    ROLE_ADMIN
}

그리고 roleType 은 일단 ROLE_USER 와 ROLE_ADMIN 으로 간단하게 구성하겠다.

이렇게 구성하고 서버를 올리면

테이블이 생성된다.

Password Encoder 등록

@Configuration
public class SecurityConfiguration {
	// 생략 
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

비밀번호 암호화에 사용할 PasswordEncoder를 securityConfiguration에 PasswordEncoder 를 Bean로 등록해준다.

Repository 생성 

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {

}

 

@Repository
public interface MemberRoleRepository extends JpaRepository<MemberRole,Long> {

}

Service 생성

@Service
@RequiredArgsConstructor
@Slf4j
public class MemberService{

    private final MemberRepository repository;
    private final MemberRoleRepository roleRepository;

    private final PasswordEncoder passwordEncoder;

    public Long save(MemberRequestDto requestDto) {
        String rawPassword = requestDto.getPassword();
        String encodePassword = passwordEncoder.encode(rawPassword);
        // 멤버 등록 및 ID 가져오기.
        Long memberId = repository.save(Member.builder()
                        .loginId(requestDto.getLoginId())
                        .name(requestDto.getName())
                        .password(encodePassword)
                        .build()).getId();
        // 권한 등록 
        roleRepository.save(
                MemberRole.builder()
                        .member(Member.builder().id(memberId).build())
                        .roleType(RoleType.ROLE_USER)
                        .build()
        );

        return memberId;
    }
}

Controller 생성

@RestController
@RequestMapping("/api/member")
@RequiredArgsConstructor
public class MemberController {

    private final MemberService service;

    @PostMapping
    public Long save(MemberRequestDto requestDto){
        return service.save(requestDto);
    }
}

 

 

@Getter
@Setter
public class MemberRequestDto {

    private String loginId;

    private String name;

    private String password;

}

 

POST localhost:8080/api/member 로 회원 등록 Request를 하면

테이블을 확인하면 Member 테이블에도 잘 쌓였고
MemberRole 테이블에도 권한이 잘 쌓인걸 볼 수 있다.

 

728x90

'IT' 카테고리의 다른 글

Expo Android App 만들기 -1  (2) 2023.06.07
Spring Custom Exception, Custom Response  (0) 2023.04.02
스프링 H2 DB 사용하기  (0) 2023.03.05
스프링 시큐리티 - 1  (0) 2023.03.02
스프링 프로젝트 초기 Gradle 과 DB 연결  (0) 2023.03.01
Comments