뷰에서 Security 정보 사용하기
개요
웹 사이트에 로그인하지 않았을 때는 상단 헤더에 로그인 버튼이 보이고 로그인을 하면 로그아웃 버튼으로 바뀌거나 내 정보를 보여준다. 어떻게 구현하는 걸까? JSP부터 Thymeleaf까지 정리해보자.
JSP와 시큐리티
방법 1
컨트롤러에서 유저의 인증정보를 사용하기 위해서는 Principal을 메소드 인자로 받아야한다. 여기에는 getName이라는 메소드가 있다.
getName은 위에서 설정한 Username값을 담고있다.
그럼 이 값을 받아서 MVC컨트롤러의 model에 추가하여 사용할 수 있다.
@GetMapping("/test")
public String test(Principal principal, Model model) {
String username = principal.getName();
model.addAttribute("username", username);
return "test";
}
<h1> Username: ${username}</h1>
유저의 정보를 단순한 String으로 뷰에 넘겨주는 방식으로 시큐리티와 뷰가 연관되어작동하지는 않는다.
만약 추가적인 정보가 더 필요하다면 컨트롤러에서 username을 이용해서 데이터베이스를 조회하고 별도의 객체에 담아서 넘겨주는 식으로 사용할 수도 있다.
방법 2
시큐리티 태그 라이브러리를 불러온다.
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
그런 다음 이렇게 사용하면 된다.
<h1>Principal : <sec:authentication property="principal"/></h1>
만약 로그인을 한 유저와 그렇지 않은 유저에게 화면을 다르게 적용하고 싶다면 이렇게 하면 된다.
<sec:authorize access="isAnonymous()">
비회원
</sec:authorize>
<sec:authorize access="isAuthenticated()">
회원
</sec:authorize>
이렇게 작성하면 된다.
위의 두 코드를 합치면 이렇게 활용할 수 있다.
<sec:authorize access="isAnonymous()">
<a href="/login">로그인</a><br>
<a href="/join">회원가입</a>
</sec:authorize>
<sec:authorize access="isAuthenticated()">
<a href="/userinfo?email=<sec:authentication property="principal.username"/>"><sec:authentication property="principal.username"/></a>
</sec:authorize>
이 외에도 다양한 조건이 있다.
표현식 | 의미 |
---|---|
hasRole([role]) | 현재 principal이 명시된 role을 가진다면 true를 리턴함 |
hasAnyRole([role1, role2]) | 현재 principal이 명시된 role들 중 하나 이상을 가진다면 true를 리턴함 |
hasAuthority([authority]) | 현재 principal이 명시된 권한을 가진다면 true를 리턴함 |
hasAnyAuthority([authority1, authority2]) | 현재 principal이 명시된 권한 중 하나 이상을 가진다면 true를 리턴함 |
principal | 현재 로그인한 유저의 정보를 담고 있는 principal 객체에 접근 |
authentication | SecurityContext로부터 Authentication 객체에 접근 |
permitAll | 모든 사용자에게 허용 |
denyAll | 모든 사용자에게 거부 |
isAnonymous() | 인증 정보가 없는 경우 true를 리턴함 |
isRememberMe() | remember-me 사용 유저라면 true를 리턴 |
isAuthenticated() | 익명 사용자가 아니라면 true를 리턴 |
isFullyAuthenticated() | remember-me와 익명 사용자에 해당하지 않으면 true를 리턴 |
hasPermission(Object target, Object permission) | 유저가 대상에 해당하는 권한이 있다면 true를 리턴 |
hasPermission(Object targetId, String targetType, Object permission) | 유저가 타겟에 해당하는 권한이 있다면 true를 리턴 |
이들은 Spring Security의 체크 애노테이션을 JSP에서도 사용할 수 있도록 내장된 표현식으로 여기 나온 기능은 컨트롤러에서도 사용할 수 있다.
예를 들면 이렇다.
@PreAuthorize("hasRole('USER')")
public String test() {
// ...
}
자세한 내용은 공식문서에서 확인할 수 있다.
Spring Security Docs
Thymeleaf
스프링부트에서는 JSP대신 Thymeleaf를 기본 템플릿 엔진으로 사용한다. 스프링 부트 프로젝트를 만들 때 의존성으로 Thymeleaf를 추가해서 사용하면 된다.
타임리프에는 일종의 히든 옵션이 존재한다. 바로 thymeleaf-extras-springsecurity6
로 타임리프만 추가했을 때는 보이지 않고 타임리프와 시큐리티를 같이 의존성에 추가하면 생기는 의존성이다.
이 의존성은 위에서 살펴본 Spring Security 태그 라이브러리와 같은 기능을 사용할 수 있게 해주는 의. 만약 시큐리티와 뷰를 연계해서 시큐리티 정보를 사용하고싶다면 이 의존성을 사용하면 된다.
의존성을 추가해줬다면 html템플릿에서 <html lang="ko" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml">
코드를 추가해준다.
JSP를 작성할 때와 동일한 방법으로 사용할 수 있는데 다소 다른 점은 태그 형식으로 사용하는 것이 아니라 태그 내에 속성을 지정하듯 사용한다는 것이다.
예를 들어 로그인한 유저의 정보를 사용하고싶다면 이렇게 할 수 있다.
<h1 sec:authentication="name"></h1>
<h1 sec:authentication="principal.authorities"></h1>
로그인한 유저와 로그인하지 않은 유저에게 다른 문구를 보여주는 건 이렇게 할 수 있다.
<a href="/login" sec:authorize="!isAuthenticated()">로그인</a><br>
<a href="/join" sec:authorize="!isAuthenticated()">회원가입</a>
<a href="/userinfo" sec:authorize="isAuthenticated()" sec:authentication="name"></a>