springboot - blog project (61-2)

Yuri Lee·2020년 9월 3일
0

springboot - blog project

목록 보기
38/49
	@PutMapping("/user")  // json 데이터를 받기 위해 @RequestBody를 사용함
	public ResponseDto<Integer> update(@RequestBody User user, 
			@AuthenticationPrincipal PrincipalDetail principal,
			HttpSession session) {
		userService.회원수정(user);
		// 여기서는 트랜잭션이 종료되기 때문에 DB 값은 변경이 됐음
		// 하지만 세션값은 변경되지 않은 상태이기 때문에 우리가 직접 세션값을 변경해줄 것임
		Authentication authentication  = 
				new UsernamePasswordAuthenticationToken(principal, null, principal.getAuthorities() );
		SecurityContext securityContext = SecurityContextHolder.getContext();
		securityContext.setAuthentication(authentication); // 강제로 세션 값을 바꿈
		
		session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1); 
	}
}

를 통해서 하려고 했지만 제대로 세션에 저장되지 않았다.
waring 이 떴다. 직접 접근을 해서 세션을 만드는 게 안된다. 예전에는 되었는데..

시큐리티 컨텍스트에 Authentication을 내가 강제로 집어넣는 것은 안될 것 같다. (인증 로직을 강제로 수행하는 방법)

따라서 AuthenticationManger에 접근을 해서 내가 login을 강제로 해버려서 Authentication를 만들면 자동으로 컨텍스트로 넣어준다. 이 방법으로 다시 수정해보자!


Authentication Manger 이 하나 필요한데 이 매니저를 SecurityConfig에서 만들어보자.

shift+alt+s -> override/implment methods

SecurityConfig.java

@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	private PrincipalDetailService principalDetailService;
	
	
	@Bean // di , AuthenticationManager를 어디에서든 사용할 수 있다. 
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		// TODO Auto-generated method stub
		return super.authenticationManagerBean();
	}

AuthenticationManager를 만들어줌

UserService.java

package com.yuri.blog.service;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.yuri.blog.model.RoleType;
import com.yuri.blog.model.User;
import com.yuri.blog.repository.UserRepository;

//스프링이 컴포넌트 스캔을 통해서 Bean에 등록을 해줌. IoC를 해준다.
@Service
public class UserService {
	
	@Autowired
	private UserRepository userRepository;
	
	@Autowired
	private BCryptPasswordEncoder encoder;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Transactional
	public void 회원가입(User user) {
		String rawPassword = user.getPassword(); //1234 원문
		String encPassword = encoder.encode(rawPassword); // 해쉬
		user.setPassword(encPassword);
		user.setRole(RoleType.USER);
		userRepository.save(user);
	}
	
	@Transactional
	public void 회원수정(User user) {
		// 수정시에는 영속성 컨텍스트 User 오브젝트를 영속화시키고, 영속화된 User 오브젝트를 수정
		// select를 해서 User 오브젝트를 DB로부터 가져오는 이유는 영속화를 하기 위해서
		// 영속화된 오브젝트를 변경하면 자동으로 DB에 update 문을 날려준다. 
		// 영속화니까 persistance 라고 하자
		// 혹시 몾찾을 수도 있으니까 , null 값일 수도 있으니까 orElseThrow를 걸어줌
		User persistance = userRepository.findById(user.getId()).orElseThrow(()->{
			return new IllegalArgumentException("회원 찾기 실패");
		});
		String rawPassword = user.getPassword();
		String encPassword = encoder.encode(rawPassword);
		persistance.setPassword(encPassword); //password 수정해주기
		persistance.setEmail(user.getEmail()); //영속화 되어있는 곳에서 setEmail를 통해 변경해준다
		// 회원수정 함수 종료시 = 서비스 종료 = 트랜잭션 종료 = commit이 자동으로 된다는 의미이다. 
		// commit 이 자동으로 된다는 것은 영속화된 persistance 객체의 변화가 감지되면, 더티체킹이 되어 변화된 것들을 업데이트 update 문을 자동으로 날려준다. 
		
		//세션 등록, authentication 객체가 만들어지면서 세션이 등록이 된다. 
		Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
		// 컨텍스트 홀더에 컨텍스트에 접근해서 authentication를 집어넣어주면 ~~ 
		SecurityContextHolder.getContext().setAuthentication(authentication);
	}
}

근데 이 방법도 안된다.


UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));

여기서 현재까지는 db 내용이 변경이 되지 않았다. 이 함수가 끝날 때 트랜잭션이 종료되면서 내용이 변경 될 것이다. 그럼 디비 내용이 변경된 후에 내가 로그인 요청을 해야 하는데 디비의 내용이 변경되기 전에 로그인 요청을 하면서 변경된 패스워드를 날리니까 로그인이 안된다. 따라서 여기서 진행하면 안된다.

UserApiController.java

package com.yuri.blog.controller.api;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.yuri.blog.config.auth.PrincipalDetail;
import com.yuri.blog.dto.ResponseDto;
import com.yuri.blog.model.RoleType;
import com.yuri.blog.model.User;
import com.yuri.blog.service.UserService;


@RestController
public class UserApiController {
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@PostMapping("/auth/joinProc")
	public ResponseDto<Integer> save(@RequestBody User user) { // username, password, email
		System.out.println("UserApiController : save 호출됨");
		userService.회원가입(user);
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1); // 자바오브젝트를 JSON으로 변환해서 리턴 (Jackson)
	}
	
	@PutMapping("/user")  // json 데이터를 받기 위해 @RequestBody를 사용함
	public ResponseDto<Integer> update(@RequestBody User user) {
		userService.회원수정(user);
		// 여기서는 트랜잭션이 종료되기 때문에 DB 값은 변경이 됐음
		// 하지만 세션값은 변경되지 않은 상태이기 때문에 우리가 직접 세션값을 변경해줄 것임
		
		//세션 등록, authentication 객체가 만들어지면서 세션이 등록이 된다. 
		Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
		// 컨텍스트 홀더에 컨텍스트에 접근해서 authentication를 집어넣어주면 된다. 
		SecurityContextHolder.getContext().setAuthentication(authentication);
		
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1); 
	}
}

여기서 해야 세션 등록이 된다. 이때 데이터베이스의 값이 변경 되어있으니까 내가 넣는 값으로 로그인을 할 수 있다.

이렇게 하면
시큐리티 컨텍스트 세션값을 직접 접근하는 것도 하고, 디비의 값이 변경되어있는 상태에서 요청을 할 것이니까...

하지만 또 waring 😀😀😀😀😀

2020-09-03 14:06:13.616 WARN 19192 --- [nio-8000-exec-8] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.security.authentication.BadCredentialsException: Bad credentials]

Bad credentials 암호가 틀렸다고? 인코딩을 해야 하는 것인가?..근데 Authentication는 다 알고 있을텐데...

UserApiController.java

package com.yuri.blog.controller.api;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.yuri.blog.config.auth.PrincipalDetail;
import com.yuri.blog.dto.ResponseDto;
import com.yuri.blog.model.RoleType;
import com.yuri.blog.model.User;
import com.yuri.blog.service.UserService;


@RestController
public class UserApiController {
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Autowired
	private BCryptPasswordEncoder encoder;

	@PostMapping("/auth/joinProc")
	public ResponseDto<Integer> save(@RequestBody User user) { // username, password, email
		System.out.println("UserApiController : save 호출됨");
		userService.회원가입(user);
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1); // 자바오브젝트를 JSON으로 변환해서 리턴 (Jackson)
	}
	
	@PutMapping("/user")  // json 데이터를 받기 위해 @RequestBody를 사용함
	public ResponseDto<Integer> update(@RequestBody User user) {
		userService.회원수정(user);
		// 여기서는 트랜잭션이 종료되기 때문에 DB 값은 변경이 됐음
		// 하지만 세션값은 변경되지 않은 상태이기 때문에 우리가 직접 세션값을 변경해줄 것임
		
		//세션 등록, authentication 객체가 만들어지면서 세션이 등록이 된다. 
		String encPassword = encoder.encode(user.getPassword());
		Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), encPassword));
		// 컨텍스트 홀더에 컨텍스트에 접근해서 authentication를 집어넣어주면 된다. 
		SecurityContextHolder.getContext().setAuthentication(authentication);
		
		return new ResponseDto<Integer>(HttpStatus.OK.value(), 1); 
	}
}

얘도 안됨..ㅋㅋ따라하는 것조차 조금 힘드네..^^..

2020-09-03 14:12:58.427 WARN 10748 --- [io-8000-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.security.authentication.BadCredentialsException: Bad credentials]

또 같은 오류..뭐가 문제일까..

사실 username을 받지 않아서 생긴 문제였다.

    update: function(){
        let data = {
                id: $("#id").val(),
                username: $("#username").val(),
                password: $("#password").val(),
                email: $("#email").val()
        };
 
        $.ajax({ 
            type: "PUT", // 수정
            url: "/user",
            data: JSON.stringify(data), // http body데이터
            contentType: "application/json; charset=utf-8",// body데이터가 어떤 타입인지(MIME)
            dataType: "json" // 요청을 서버로해서 응답이 왔을 때 기본적으로 모든 것이 문자열 (생긴게 json이라면) => javascript오브젝트로 변경
        }).done(function(resp){
            alert("회원 수정이 완료되었습니다.");
            //console.log(resp);
            location.href = "/";
        }).fail(function(error){
            alert(JSON.stringify(error));
        }); 
        
    },

고작 이 하나 때문에.......

profile
Step by step goes a long way ✨

0개의 댓글