일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- kotest testcontainers
- Invalid property 'principal.username' of bean class
- DI
- spring aop
- multimodule testcontainers
- java
- 낙관적 락 롤백
- 낙관적 락 재시도
- @transactional
- 우아한 테크러닝
- Spring Cloud Gateway
- 알고리즘
- netty
- OptimisticLock
- 백준
- aop
- S3
- 소수찾기 java
- 멀티모듈 테스트컨테이너
- interface
- ObjectOptimisticLockingFailureException
- spring DI
- jpa
- AccessToken
- TestContainers
- ObjectOptimisticLockingFailureException 처리
- 형상관리
- springsecurity
- redissonlock aop
- RefreshToken
- Today
- Total
조급하면 모래성이 될뿐
[SpringSecurity] Invalid property 'principal.username' of bean class 본문
[SpringSecurity] Invalid property 'principal.username' of bean class
Pawer0223 2020. 2. 19. 10:22SpringSecurity를 적용하면서 로그인 성공시 계정 정보를 가지고 오기 위해서 SpringSecurity에서 제공해주는 태그를 사용해서 아래와 같이 principal.username변수로 가지고 오고 있었는데, error가 발생하였다.
<sec:authorize access="isAuthenticated()">
<div class="form-group" align="center">
<h5>
<sec:authentication property="principal.username" />님, 반갑습니다.
</h5>
<br>
<sec:authorize access="isAuthenticated()">
<form action="/logout" method="POST">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<button type="submit" class="btn btn-dark btn-sm">LOGOUT</button>
</form>
</sec:authorize>
</div>
</sec:authorize>
Error
Invalid property 'principal.username' of bean class [org.springframework.security.authentication.UsernamePasswordAut henticationToken]: Bean property 'principal.username' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
[ 참조 ] 해당 링크에서 해결책을 도출할 수 있었다..
일단 위의 코드에서 문제는 principal객체가 null로 넘어오고 있었는데.. 아래 코드를 추가해서 printLn을 찍어보니깐.. 반환이 String으로 username값만 담겨서 반환이 되고 있었다..
<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %>
<%@ page import="org.springframework.security.core.Authentication" %>
<%@ page import="com.boot.test1.vo.Account" %>
<%
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
String name = "";
if ( principal instanceof Account ) System.out.println(" Accout 객체 맞음 !! ");
else System.out.println(" Accout 객체 아님.. " + principal.getClass().toString());
if ( principal != null && principal instanceof Account ){
name = ((Account)principal).getUsername();
System.out.println(" Account에서 가지고온 이름 ! : " + name);
}else {
name = (String)principal ;
System.out.println(" 그냥 String.. ㅜㅜ : " + name);
}
%>
<h5><%=name%>님, 반갑습니다. </h5>
우선, 화면에서 계정정보를 가져오기위해 작성한 코드는 SpringSecurity에서 지원해주는 기능을 활용한 것인데, 로그인 정보를 가져올 때 아래와같은 특징이 있다고한다.
- 계정 정보를 가진 Principal의 객체를 가져오기 위 호출되는 auth.getPrincipal() 함수의 return 값은 2가지로 분류된다.
1) 인증을 하지 않았다면 anonymousUser라는 문자열(String) 객체
2) 인증에 성공했다면 로그인한 사용자 정보가 들어있는 객체!
( UserDetails를 구현하고 있는 우리가 정의한 vo객체, 내 코드로는 Account객체! )
!! 그리고 로그인 성공할 때는 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 객체를 받아온다고 한다!!!!!!!!!!!!!!!!!!!!!!!!!!!!
저 문구를 보니깐.. 내가 AuthenticationProvider 구현 클래스 정의하면서.. authenticate() 메서드의 반환 값으로 저 객체를 return해주었던게 생각이났다..
그래서 확인을 해보니 UsernamePasswordAuthenticationToken 객체를 return할 때 생성자의 인자 값으로 그냥
[ username, password, 권한정보 ] 를 전달해 주었는데 여기서 username으로 전달하 인자 값이 principal객체를 초기화하는데 참조되는 값이었던것이다..
아래는 UsernamePasswordAuthenticationToken 의 생성자 코드이다.
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
그래서 아! 내가 저 객체를 만들면서 뭔가 잘못 던졌구나..라는 생각에 인자 값을 String이 아닌 Account객체로 변경해주었다.
ㅇAS-IS
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
log.info("### authenticate ### ");
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
Account account = (Account) accountService.loadUserByUsername(username);
return new UsernamePasswordAuthenticationToken(username, password, account.getAuthorities());
}
ㅇTO-BE
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
log.info("### authenticate ### ");
String username = (String) authentication.getPrincipal();
Account account = (Account) accountService.loadUserByUsername(username);
return new UsernamePasswordAuthenticationToken(account, account, account.getAuthorities());
}
이렇게 변경을 해주니 로그인 성공했을 때, 사용자 정보를 화면에서 잘 출력해주었다!! ㅠㅠ
( <sec:authentication > 태그를 사용해도 잘되고, 직접 auth.getPrincipal();해서 변수로 해도 잘된다..! )
정리
Principal()객체를 활용하여 화면에 로그인 정보를 출력할 때 에러사항이 존재한다면, 우리가 Security에서 UsernamePasswordAuthenticationToken객체정보를 customize한 부분을 확인해보자...