전 포스팅에서 AWS를 사용하여 서버를 구축하고 githup action을 통하여서 자동배포를 만들어 줬습니다.
그리고 오늘부터 올리는 프로젝트는 계속 깃허브에 커밋할 예정입니다. 확인하실분은 확인해주세요.
--수정--
제가 계속 react를 통해서 view단을 만드려고 했는데, 아직 제가 프론트 부분이 너무 부족해서 구현이 힘들어서
spring boot의 툴인 thymleaf를 사용하도록 하겠습니다 ㅠ
일단 JWT 토큰 형식으로 로그인 하는것 보다 spring security 를 통해 로그인을 한 후 나중에 JWT 토큰 형식으로 변경 하겠습니다.
일단 디렉터리 구조는 이렇게 간단하게 작성했습니다.
일단 코드를 올려드리고 설명 드리겠습니다.
@Controller
@AllArgsConstructor
public class MemberController {
private MemberService memberService;
@GetMapping("/")
public String index(){
return "/index";
}
// 회원가입 페이지
@GetMapping("/user/signup")
public String dispSignup() {
return "/signup";
}
// 회원가입 처리
@PostMapping("/user/signup")
public String execSignup(MemberRequest memberRequest) {
memberService.joinUser(memberRequest);
return "redirect:/user/login";
}
// 로그인 페이지
@GetMapping("/user/login")
public String dispLogin() {
return "/login";
}
// 로그인 결과 페이지
@GetMapping("/user/login/result")
public String dispLoginResult() {
return "/loginSuccess";
}
// 로그아웃 결과 페이지
@GetMapping("/user/logout/result")
public String dispLogout() {
return "/logout";
}
// 접근 거부 페이지
@GetMapping("/user/denied")
public String dispDenied() {
return "/denied";
}
// 내 정보 페이지
@GetMapping("/user/info")
public String dispMyInfo() {
return "/myinfo";
}
// 어드민 페이지
@GetMapping("/admin")
public String dispAdmin() {
return "/admin";
}
}
유선 프론트 단에서 요청을 받을때 컨트롤단 을 작성했습니다.
원래는 @RestController 어노테이션을 사용하여 RestAPI를 만드려고 했는데,
일단 기본적인 MVC 구조를 이용하여 만든 후에 REST API 로 넘어가려고 한다.
@Getter
@Entity
@Table(name = "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 20, nullable = false)
private String email;
@Column(length = 20, nullable = false)
private String password;
@Builder
public Member(Long id, String email, String password) {
this.id = id;
this.email = email;
this.password = password;
}
}
제일 중요한 entity부분입니다.
JPA를 통하여 만들기 때문에 실질적인 DB를 담당하는 부분입니다.
쉽게 생각하면 기존에 사용하던 DTO 와 다르게 그냥 DB컬럼이라고 생각하시면 됩니다.
여기에는 기존에 있던 @Setter가 없는데
이 글을 보면 확실하게 이해가 갈텐데 한마디로 정리하자면
DB는 객체의 일관성을 유지해야하는데, 이 Setter를 사용하게 되면 그 일관성을 해치게 됩니다.
@AllArgsConstructor
@Getter
public enum Role {
ADMIN("ROLE_ADMIN"),
MEMBER("ROLE_MEMBER");
private String value;
}
@Getter
@Setter
@ToString
@NoArgsConstructor
public class MemberRequest {
@Id
private Long id;
private String email;
private String password;
private LocalDateTime createdDate;
private LocalDateTime modifiedDate;
@Builder
public MemberRequest(Long id, String email, String password){
this.id = id;
this.email = email;
this.password = password;
}
public Member toEntity(){
return Member.builder()
.id(id)
.email(email)
.password(password)
.build();
}
}
우리가 흔히 사용하던 DTO 입니다. VO라고 부르기도 하는데 저는 부르기 편하게 DTO 를 사용했습니다.
public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByEmail(String userEmail);
}
JPARepository를 상속받아서 Repository를 생성하였습니다.
@Service
public class MemberService implements UserDetailsService {
private MemberRepository memberRepository;
@Transactional
public Long joinUser(MemberRequest memberRequest) {
// 비밀번호 암호화
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
memberRequest.setPassword(passwordEncoder.encode(memberRequest.getPassword()));
return memberRepository.save(memberRequest.toEntity()).getId();
}
@Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
Optional<Member> memberWraper = memberRepository.findByEmail(userEmail);
Member member = memberWraper.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if (("admin@example.com").equals(userEmail)) {
authorities.add(new SimpleGrantedAuthority(Role.ADMIN.getValue()));
} else {
authorities.add(new SimpleGrantedAuthority(Role.MEMBER.getValue()));
}
return new User(member.getEmail(), member.getPassword(), authorities);
}
}
제일 중요한 Service단 입니다.
html 부분은 제일 하단에 제가 첨부한 레퍼런스를 참고하시길 바랍니다.
사실 엄청 간단하게 로그인 로직을 만들어봤는데 밑에 링크는 스프링 시큐리티를 이용한 로그인 로직입니다.
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private MemberService memberService;
//비밀번호 암호화
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception
{
// static 디렉터리의 하위 파일 목록은 인증 무시 ( = 항상통과 )
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 페이지 권한 설정
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/myinfo").hasRole("MEMBER")
.antMatchers("/**").permitAll()
.and() // 로그인 설정
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/user/login/result")
.permitAll()
.and() // 로그아웃 설정
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.logoutSuccessUrl("/user/logout/result")
.invalidateHttpSession(true)
.and()
// 403 예외처리 핸들링
.exceptionHandling().accessDeniedPage("/user/denied");
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService).passwordEncoder(passwordEncoder());
}
}
위에 WebSecurityConfigurerAdapter를 상속받을때 문제가 생기는데
사실 상관하지 않아도 됩니다. 간단하게 말하자면 버전문제입니다.
사실상 Java 의 모든 코드는 생성했습니다.
아마 구동해보시면 잘 돌아가실겁니다.
혹시몰라서 dependecy까지만 올려드리고 다음으로 넘어가겠습니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
jwt 부분은 무시하셔도 좋습니다.
DB부분은 직접 찾아서 공부해 보시는걸 추천드립니다.(너무 간단한 로직이라,..)
레페런스
https://victorydntmd.tistory.com/328
https://spring.io/guides/topicals/spring-security-architecture#_web_security
'개인프로젝트' 카테고리의 다른 글
[Spring boot 중고거래 쇼핑몰 생성] 프로젝트 명세서 (0) | 2022.11.19 |
---|---|
[Spring boot 중고거래 쇼핑몰 생성] 1. 기획 (2) | 2022.10.11 |