[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기4 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++

 * 초기 세팅 (개발환경) 및 설정 

https://rockbottomdevbus.blogspot.com/2024/04/springboot-1-325-intellij-idea-20241.html


* 이전글 

[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기1 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25  )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++

https://rockbottomdevbus.blogspot.com/2024/05/springboot-spring-security-1-login-325.html

[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기2 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++

https://rockbottomdevbus.blogspot.com/2024/05/springboot-spring-security-2-login-325.html

[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기3 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++

https://rockbottomdevbus.blogspot.com/2024/05/springboot-spring-security-3-login-325.html



1. Custom403Handler.java 생성 ( Accesed Denied 오류 핸들링 

1. handler패키지 생성 >> 경로 : /main/java/{pakage name}/security/handler/

-- Custom403Handler.java

package org.zerock.b01.security.handler;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import java.io.IOException;

@Log4j2
public class Custom403Handler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.info("----------------Accesed Denied--------------");


response.setStatus(HttpStatus.FORBIDDEN.value());

//JSON 요청이었는지 확인
String contentType = request.getHeader("Content-Type");
log.info("Content-Type: " + contentType);
boolean jsonRequest = contentType.startsWith("application/json");

log.info("isJSON" + jsonRequest);

//일반 request
if (!jsonRequest) {
response.sendRedirect("/member/login?error=ACCESS_DENIED");
}
}
}


2. CustomSecurityConfig.java에 exceptionHandler 설정 추가

1. CustomSecurityConfig.java의 filterChain메서드에 설정추가

-CustomSecurityConfig.java

//exceptionHandler 설정
http.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> {
httpSecurityExceptionHandlingConfigurer.accessDeniedHandler(accessDeniedHandler());
});

2. CustomSecurityConfig.java에 Bean등록

-CustomSecurityConfig.java

//AccessDeniedHandler 빈등록
@Bean
public AccessDeniedHandler accessDeniedHandler(){
return new Custom403Handler();
}


3. 로컬 화면에서 테스트 동일한  

권한없는 계정으로 modify에 강제로 접근해서 수정기능 접근 

http://localhost:8090/board/modify?bno=126&page=8&size=10&keyword=&continue


4. Member.java 생성

1. Member.java 파일 생성 >> 경로 : /main/java/{pakage name}/domain

-- Member.java

package org.zerock.b01.domain;

import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import lombok.*;

import java.util.HashSet;
import java.util.Set;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "roleSet")
public class Member extends BaseEntity {

@Id
private String mid;

private String mpw;

private String email;

private boolean del;

//열거형 처리 roleSet
@ElementCollection(fetch = FetchType.LAZY)
@Builder.Default
//중복되면안되는이유로 set사용
private Set<MemberRole> roleSet = new HashSet<>();

//소셜 로그인 처리
private boolean social;

//패스워드 변경 (세터 대신)
public void changePassword(String mpw) {
this.mpw = mpw;
}

//이메일 변경
public void changeEmail(String email) {
this.email = email;
}

//삭제 여부 변경 (바로삭제 x)
public void changeDel(boolean del) {
this.del = del;
}

//addRoll 역할 추가
public void addRole(MemberRole role) {
this.roleSet.add(role);
}

//역할 삭제를 위해서
public void clearRoles(){
this.roleSet.clear();
}

//소셜 여부 변경을 위해서
public void changeSocial(boolean social) {
this.social = social;
}

}


5. MemberRole.java 생성 ( 열거형으로 생성 public enum : 상수형 배열의 개념으로 생각하면됨) 

1. MemberRole.java 파일 생성 >> 경로 : /main/java/{pakage name}/domain

-- MemberRole.java

package org.zerock.b01.domain;

//상수형 배열이라고 생각하면 됨
public enum MemberRole {
USER, ADMIN;
}


6. MemberRepository.java 생성 (소셜을 사용하지 않는 사용자의 권한 세팅)

1. MemberRepository.java 파일 생성 >> 경로 : /main/java/{pakage name}/repository

-- MemberRepository.java

package org.zerock.b01.repository;

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.zerock.b01.domain.Member;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, String> {

// 소셜 사용하지 않는 사용자의 권한 로딩
@EntityGraph(attributePaths = "roleSet") //조인을 위해서 사용한 어노테이션
@Query("select m from Member m where m.mid = :mid and m.social = false ")
Optional<Member> getWithRole(String mid);

}


8. MemberRepositotyTests.java 생성 ( Repository 테스트 )

1. MemberRepositotyTests.java 파일 생성 >> 경로 : /test/java/{pakage name}/repository

-- MemberRepositotyTests.java

package org.zerock.b01.repository;

import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.zerock.b01.domain.Member;
import org.zerock.b01.domain.MemberRole;

import java.util.Optional;
import java.util.stream.IntStream;

@SpringBootTest
@Log4j2
public class MemberRepositotyTests {

@Autowired
private MemberRepository memberRepository;

@Autowired
private PasswordEncoder passwordEncoder;

@Test
public void insertMember(){
IntStream.rangeClosed(1,100).forEach(i -> {
Member member = Member.builder()
.mid("member"+i)
.mpw(passwordEncoder.encode("1111"))
.email("email"+i + "@test.com")
.build();
member.addRole(MemberRole.USER);

if(i >= 90){
member.addRole(MemberRole.ADMIN);
}
memberRepository.save(member);
});

}


@Test
public void testRead(){
Optional<Member> result = memberRepository.getWithRole("member100");
Member member = result.orElseThrow();
log.info("11111111111111111");
log.info(member);
log.info("22222222222222222222");
log.info(member.getRoleSet());
log.info("333333333333333333");
member.getRoleSet().forEach(memberRole -> log.info(memberRole.name()));

}
}


9. 쿼리 확인 

select * from member;

select * from member_role_set where role_set = 0;
select * from member_role_set where role_set = 1;


10. CustomUserDetailsService 파일 수정 

( 권한세팅 및 DB에 저장된 USER로 로그인 하기 위한 설정 ) 

1. 기존에 강제로 로그인처리했던 user1에 관련된 설정 주석처리 

2. db 사용자정보 저장 및 유효성 처리 

3. dto를 생성하여 값을 세팅, UserDetails 타입으로 리턴함

-CustomUserDetailsService.java


@Log4j2
@Service
@RequiredArgsConstructor
class CustomUserDetailsService implements UserDetailsService {

//private PasswordEncoder passwordEncoder; // 주입하면 순환구조 발생 final 제거
private final MemberRespository memberRespository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("loadUserByUsername : " + username);

//userDeatils 객체로 반환하는 userDetails를 생성 (모든 로그인처리가 user1로 처리됨)
/*UserDetails userDetails = User.builder().username("user1")
.password(passwordEncoderConfig.passwordEncoder().encode("1234")).authorities("ROLE_USER").build();
return userDetails;*/

// DB에 등록된 사용자 정보를 불러옴....
Optional<Member> result = memberRespository.getWithRoles(username);

// 결과가 없는 경우에... UserDetails에 있는 예외 처리 클래스를 호출....
if(result.isEmpty()) {
throw new UsernameNotFoundException("username not found.....");
}

Member member = result.get();

//UserDetails 객체로 반환하는 userDetails를 생성...
MemberSecurityDTO memberSecurityDTO = new MemberSecurityDTO(
member.getMid(),
member.getMpw(),
member.getMname(),
member.getMnick(),
member.getMemail(),
member.getMpno(),
member.isDel(),
false, // 소셜로 로그인 처리하지 않는 상황
member.getRoleSet().stream().map(memberRole ->
new SimpleGrantedAuthority("ROLE_"+memberRole.name()))
.collect(Collectors.toList())

);

log.info("memberSecurityDTO : ");
log.info(memberSecurityDTO);

return memberSecurityDTO;
}


11. db에 저장된 사용자로 로그인 되는지 테스트

http://localhost:8090/member/login


댓글

T O P