Authentication이란?
Spring Security에서 사용자를 나타내는 중요한 객체
Spring Security에서는 사용자의 인증 및 인가 정보를 Authentication객체에 담는다.
즉 이 객체는 현재 사용자를 나타내는 핵심 객체이며, 사용자가 인증되면 이 객체가 생성되어야 한다.
@AuthenticationPrincipal 이란?
@AuthenticationPrincipal 어노테이션을 사용하면 세션에 저장된 현재 사용자의 정보를 쉽게 찾을 수 있다.
예를 들어 로그인 후 세션에 저장된 사용자 정보를 Authentication 객체를 통해 가져오려면 일련의 과정을 거쳐야한다.
그러나 @AuthenticationPrincipal을 사용하면 이 과정을 간소화할 수 있다.
@GetMapping("/user/{id}/update")
public String update(@PathVariable int id,
// 해당 세션 접근
@AuthenticationPrincipal PrincipalDetails principalDetails ) {
// 추천
System.out.println("세션정보: " + principalDetails.getUser());
// 극혐
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalDetails mPrincipalDetails = (PrincipalDetails)auth.getPrincipal();
System.out.println("직접 찾은 세션정보: "+mPrincipalDetails.getUser());
return "user/update";
}
로그인 후 세션에 저장된 사용자 정보를 Authentication 객체를 통해 가져오려면 일련의 과정
- /auth/signin으로 POST 요청이 발생하면 Spring Security는 POST 요청을 인식하고, 해당 요청을 낚아채서 자체적인 인증 프로세스를 진행한다.
- 이때, 낚아챈 요청은 PrincipalDetailsService로 전달된다. PrincipalDetailsService는 Spring Security에서 제공하는 UserDetailsService 인터페이스를 구현한 클래스로, 사용자 정보를 조회하고 인증을 처리한다.
- PrincipalDetailsService 내부에서는 전달받은 username을 사용하여 해당 사용자 정보를 조회(인증)한다. 만약 해당 사용자가 존재하지 않으면 인증에 실패하고 예외를 발생시킨다.
- 사용자가 존재한다면, 해당 사용자 정보를 단순히 담은 PrincipalDetails 객체를 생성한다.
- 이 PrincipalDetails 객체를 Spring Security가 인식하고 관리하게 하려면 Authentication 객체에 포함시켜야한다. 인증된 사용자라는 뜻이다. 이 Authentication 객체는 세션 영역( 현재 사용자의 상태를 유지하는 데 사용되는 메모리 공간 ) 안의 SecurityContextHolder라는 객체에 저장된다. 즉 SecurityContextHolder 객체 안에 Authentication이 있다.
- 이렇게 인증된 사용자 정보를 찾기 위해서는 @AuthenticationPrincipal 어노테이션을 사용하면 세션 영역에 저장된 Authentication 객체를 쉽게 가져올 수 있으며, 그 안에 포함된 PrincipalDetails를 찾을 수 있다.
따라서 @AuthenticationPrincipal을 사용하면 별도의 코드 없이도 세션에 저장된 사용자 정보에 접근할 수 있어서 개발 과정이 간소화된다는 장점이 있다.
pom.xml에 시큐리티 태그 라이브러리 추가
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
header는 공통으로 들어가므로 header.jsp에 인증정보를 가져와 공통으로 여러 페이지에 적용시킬 수 있도록 하였다.
header.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<sec:authorize access="isAuthenticated()">
<sec:authentication property="principal" var="principal"/>
</sec:authorize>
<div class="item__input">
<input type="text" name="name" placeholder="이름"
value="${principal.user.name}" />
</div>
// (1) 회원정보 수정
function update(userId, event) {
let data = $("#profileUpdate").serialize();
console.log(data);
// 데이터를 응답해줘야한다.
$.ajax({
type: "put",
url: "/api/user/" + userId,
data: data,
contentType: "application/x-www-form-urlencoded; charset=utf-8", // contentType의 오타 수정
dataType: "json"
}).done(res => {
console.log("update 성공");
location.href ="/user/"+ userId;
}).fail(error => {
console.log("update 실패");
});
}
참고!
직렬화(Serialize) : 데이터를 일련의 바이트 스트림으로 변환하는 과정
자바스크립트에서는 주로 jQuery의 serialize() 함수를 사용하여 HTML 폼 요소의 입력 값을 URL 인코딩된 문자열로 반환한다.
직렬화 장점 : 직렬화된 데이터를 사용하면 자바스크립트에서 폼 데이터를 쉽게 추출하고 전송한다.
ex) name=%EC%8C%80&username=ssar&password=&website=&bio=123&email=ssar%40nate.com&tel=&gender=23
회원정보 수정 과정을 담은 자바 코드
UserApiController
@RequiredArgsConstructor
@RestController
public class UserApiController {
private final UserService userService;
// 데이터 트랜스젝션 오브젝트
@PutMapping("/api/user/{id}")
public CMRespDto<?> update(@PathVariable int id,UserUpdateDto userUpdateDto
,@AuthenticationPrincipal PrincipalDetails principalDetails){
User userEntity = userService.회원수정(id, userUpdateDto.toEntity());
principalDetails.setUser(userEntity);// 세션정보 변경
return new CMRespDto<>(1,"회원수정완료", userEntity);
}
}
UserUpdateDto
@Data
public class UserUpdateDto {
private String name; //필수값
private String password; //필수값
private String website;
private String bio;
private String phone;
private String gender;
// 필수값이 아닌값까지 고려해서 Entity를 만드는 것은 위험함
// 사용자가 이름,패스워드 기재 안하면 DB에 공백이 들어가므로 validation체크해야함
public User toEntity() {
return User.builder()
.name(name) // Validation 체크
.password(password) // Validation 체크
.website(website)
.bio(bio)
.phone(phone)
.gender(gender)
.build();
}
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public class CMRespDto<T> {
private int code; // 1(성공),-1(실패)
private String message;
private T data;
}
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Transactional
public User 회원수정(int id,User user) {
// 1. 영속화
// (1) get(): 무조건 찾았다.걱정마
// (2) orElseThrow(): 못찾았으니 exception 발동
User userEntity = userRepository.findById(id).get();
// 2. 영속화된 오브젝트 수정
userEntity.setName(user.getName());
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
userEntity.setPassword(encPassword);
userEntity.setBio(user.getBio());
userEntity.setWebsite(user.getWebsite());
userEntity.setPhone(user.getPhone());
userEntity.setGender(user.getGender());
return userEntity;
// 수정이 끝나면 더티체킹이 일어나서 업데이트 완료
}
}
'프로젝트 > SNS 프로젝트' 카테고리의 다른 글
[회원정보 수정] 유효성 검사 (0) | 2024.03.29 |
---|---|
[회원정보 수정] 영속화 / 세션 정보 수정 (0) | 2024.03.29 |
[로그인] UserDetailService (0) | 2024.03.28 |
[회원가입] 전처리 후처리 개념 및 validation (0) | 2024.03.26 |
[회원가입] bcrypt를 통한 비밀번호 암호화 (0) | 2024.03.26 |