로그인 중인 유저 정보 수정 시 Spring security Authentication/HttpSesession 값 반영

아무튼 간에·2022년 5월 12일
1

개발환경

OS: Windows 10
IDE: eclipse 2022-03
JAVA: 17


목표

로그인 중인 유저의 정보(권한 등)를 클라이언트 단에서 수정 시 변경된 정보를 Spring Security로 인증해서 HttpSession에도 반영시키자~!


해결

- 컨트롤러

반드시 새로운 Authentication을 생성해서 SecurityContextHolder에 설정해주어야 함.

@Transactional 
@RequestMapping("/url") 
public Map<String, Object> modify(@RequestBody Map<String, Object> param) {

    // 1. 유저 정보 업데이트 쿼리 실행 (소스 생략)
    .
    .
    .
    
    // 2. 현재 Authentication에 저장된 account의 age값 변경
    // 2-1. 현재 Authentication 정보 호출
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
	UserAccount userAccount = (UserAccount) authentication.getPrincipal();
    
    // 2-2. 현재 Authentication로 사용자 인증 후 새 Authentication 정보를 SecurityContextHolder에 세팅
    SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(authentication,userAccount.getUsername()));

	.
    .
    .
    
    return map;
}
  • createNewAuthentication(Authentication, userAccount.getUsername()) : 새로운 Authentication 객체를 리턴

- 새로운 인증 생성

/**
  * @description 새로운 인증 생성  
  * @param currentAuth 현재 auth 정보
  * @param username	현재 사용자 Id
  * @return Authentication
  * @author Armton
*/
protected Authentication createNewAuthentication(Authentication currentAuth, String username) {
    UserDetails newPrincipal = accountService.loadUserByUsername(username);
    UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(newPrincipal, currentAuth.getCredentials(), newPrincipal.getAuthorities());
    newAuth.setDetails(currentAuth.getDetails());
    return newAuth;
}

UsernamePasswordAuthenticationToken: SecurityContextHolder.getContext()에 등록될 Authentication 객체

  • newPrincipal : 현재 로그인 된 사용자의 username을 이용해 이미 업데이트 된 사용자 조회 및 바인딩
  • newAuth : 사용자의 ① 새로운 정보(newPrincipal)와 ② 다시 조회된 사용자 권한(newPrincipal.getAuthorities())과 ③ 아직 업데이트 되지 않은 현재 사용자의 자격 증명(currentAuth.getCredentials())을 통해 인증된 Authentication 객체를 생성
  • newAuth.setDetails() : 새 인증 객체에 기존 details 바인딩

- AccountService.java

@Service
public class AccountService implements UserDetailsService {
	
	@Autowired
	AccountDao accountDao;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		Account account = accountDao.getAccountById("account.getAccountById", username); // DB에서 UserId로 사용자 조회
		if (account == null) {
			throw new UsernameNotFoundException(username);
		}
		return new UserAccount(account);
	}

- 참고

UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities)


그 외 다양한 시도들

1.

AuthenticationManager 객체로 UsernamePasswordAuthenticationToken 객체를 익명으로 생성하여 바로 바인딩 시도.

Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(account.getId(), account.getPassword()));

-> 안됨. Failed to authenticate since password does not match stored value

2.

1번에서 생성된 Authentication 객체를 SecurityContextHolder에 등록

 Authentication authentication1 = authenticationManager.authenticate(authentication);
	        SecurityContextHolder.getContext().setAuthentication(authentication1);

-> 안됨. Failed to authenticate since no credentials provided


참고

profile
armton garnet

1개의 댓글

comment-user-thumbnail
2023년 8월 2일

덕분에 도움을 받았습니다. 감사합니다

답글 달기