본문 바로가기

React

bmsapi 로그인 기능 구현

1. pom.xml에 dependency 추가

  • jjwt maven
  • spring security
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

 

2. 기본패키지에 WebSecurityConfigurerAdapter를 상속하는

SecurityConfig클래스 생성

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
		.cors() // cors에 대해 감시하겠음.
		.and()
		.csrf().disable() // frontend와 backend가 분리된 상황에서는 일반적으로 csrf감시를 안 함.
		.sessionManagement() // session: 전통적인 로그인 지원방식으로 서버에 인증자에 대한 정보를 저장함.
		.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 전통적인 로그인 방식이 아니므로 session을 생성하지 않음.
		.and()
		.authorizeRequests() // 요청에 대해 인증을 하도록 함.
		.antMatchers("/","/api/auth/**").permitAll() // /와 /api/auth/로 시작하는 요청은 프리패스
		.anyRequest() // 그 외의 요청에 대해서는 
		.authenticated(); // 인증을 완료했을 때만 이용 가능.
	}
	
    // 나중에 비밀번호 암호화나
    // 로그인 시 입력한 암호화 db에 저장된 암호가 일치하는지 확인할 때 사용함.
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

 

3. TokenProvider 클래스 생성

@Component // 객체화 하기위해 선언
public class TokenProvider {
	
	private static final String SECRET_KEY="dsaf1313eaf"; // 비밀키: 아무거나 길게 작성
	
	public String create(MemberEntity memberEntity) {
		// 지금으로부터 1시간 후의 값이 expire에 들어감.
		Date expire = Date.from(Instant.now().plus(1, ChronoUnit.HOURS)); 
		
		return Jwts.builder()
		.signWith(SignatureAlgorithm.HS512, SECRET_KEY) // 헤더+페이로드+비밀키를 이용해 암호화할 때 HS512알고리즘 사용
		.setSubject(memberEntity.getId())// 토큰 작성자의 아이디: username은 외부에 노츨되어 있어 노출되지 않은 id 사용
		.setIssuer("bmsapi") // 토큰이 사용될 앱
		.setIssuedAt(new Date()) // 토큰이 생성된 시각
		.setExpiration(expire) // 토큰이 만료되는 시각
		.compact(); // build()가 아니라 compact()함수를 이용해서 문자열 객체를 만듦.
	}
}

 

4. MemberService에 login메서드 생성

	@Autowired 
	private TokenProvider tokenProvider;
	@Autowired
	private PasswordEncoder passwordEncoder; // 나중에 사용
	
    // 나중에 비밀번호를 암호화하면, username과 password를 동시 인증이 불가능
    // 따라서 동시에 넣어 인증하지 않고 해당 회원이 있는지 여부만 확인
	public MemberDTO login(MemberDTO dto) {
		MemberEntity member =  getByCredentials(dto.getUsername());
		
		if(member == null) {
			throw new RuntimeException("해당 회원이 없어서 로그인 거부");
		}
		
        // 입력받은 비밀번호와 db에 저장된 비밀번호가 일치하는지 확인
        // 나중에 비밀번호를 암호화하면 여기서 passwordEncoder.match()메서드를 이용한다.
		if(!member.getPassword().equals(dto.getPassword())) {
			throw new RuntimeException("비밀번호가 안 맞아서 로그인 거부");
		}
		
		String token = tokenProvider.create(member);
		
		dto = new ModelMapper().map(member, MemberDTO.class);
		dto.setToken(token);; // MemberDTO에 private String token; 추가
		
		return dto;
	}
-------------------------------------------------------------------------------
	public MemberEntity getByCredentials(String username) {
		return memberRepository.findByUsername(username);
	}

 

5. AuthController 생성

 - MemberController의 insert핸들러를 잘라내어 붙여넣기

    -> 인증없이 insert작업이 가능

 - login핸들러 생성(PostMapping)

@RestController
@RequestMapping("/api/auth")
public class AuthController {
	
	@Autowired
	public MemberService memberService;
	
	@PostMapping("/login")
	public ResponseEntity<?> login(@RequestBody MemberDTO dto){
		Map<String, Object> map = new HashMap<>();
		
		if(dto == null) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(dto.getUsername() == null || dto.getUsername().equals("")) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(dto.getPassword() == null || dto.getPassword().equals("")) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}

		try {
			dto = memberService.login(dto);
			map.put("result", dto);
			return ResponseEntity.ok().body(map);
		} catch (Exception e) {
			e.printStackTrace();
			map.put("result", "로그인 실패");
			return ResponseEntity.badRequest().body(map);
		}
		
	}
	
	@PostMapping("/insert")
	public ResponseEntity<?> insert(@RequestBody MemberDTO dto){
		Map<String, Object> map = new HashMap<>();
		
		if(dto == null) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(dto.getUsername() == null) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(dto.getName() == null) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(dto.getPassword() == null) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(dto.getPassword2() == null) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		if(!(dto.getPassword().equals(dto.getPassword2()))) {
			map.put("result", "잘못된 데이터");
			return ResponseEntity.badRequest().body(map);
		}
		
		try {
			dto = memberService.insert(dto);
			map.put("result", dto);
			return ResponseEntity.ok().body(map);
		} catch (Exception e) {
			e.printStackTrace();
			map.put("result", "입력 실패");
			return ResponseEntity.badRequest().body(map);
			
		}
	}
}

 

6. vscode에서 MemberInsert.js 수정

 
insertFetchfn("member", dto);
--->
insertFetchfn("auth", dto);

 - NetworkUtils.js에서 insertFetchfn 수정

export function insertFetchfn(servicename, dto){
    return fetchFn("POST", `http://localhost:9005/api/${servicename}`, dto)
    .then(data =>{
        console.log(data.result);
        if(servicename === "auth"){
            servicename = "member";
        }

        let what = "data.result.id";

        if(servicename === "member"){
            what = data.result.username;
        }
        
        window.location.href = `/${servicename}/detail/${what}`;
    })
}