조급하면 모래성이 될뿐

[SpringSecurity] Invalid property 'principal.username' of bean class 본문

TroubleShooting

[SpringSecurity] Invalid property 'principal.username' of bean class

Pawer0223 2020. 2. 19. 10:22

SpringSecurity를 적용하면서 로그인 성공시 계정 정보를 가지고 오기 위해서 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한 부분을 확인해보자...

 

반응형