@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));
});
},
고작 이 하나 때문에.......