Spring Security 에서 현재 인증된(로그인한) 사용자의 정보를 가져오는 방법 에 대해 살펴볼 것 입니다. 스프링의 다양한 메카니즘을 통해 현재 로그인 중인 사용자의 정보를 가져올 수 있는데, 대표적인 몇 가지를 살펴보겠습니다.
1. Bean 에서 사용자 정보 얻기
가장 간단한 방법은 전역에 선언된 SecurityContextHolder을 이용하여 가져오는 방법입니다.
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserDetails userDetails = (UserDetails)principal; String username = principal.getUsername(); String password = principal.getPassword();
2. Controller 에서 사용자 정보 얻기
@Controller로 선언된 bean 객체에서는 메서드 인자로 Principal 객체에 직접 접근할 수 있는 추가적인 옵션이 있습니다.
@Controller public class SecurityController { @GetMapping("/username") @ResponseBody public String currentUserName(Principal principal) { return principal.getName(); } }
principal 객체 뿐만 아니라 authentication 토큰 또한 사용할 수 있습니다.
@Controller public class SecurityController { @GetMapping("/username") @ResponseBody public String currentUserName(Authentication authentication) { UserDetails userDetails = (UserDetails) authentication.getPrincipal(); return userDetails.getUsername(); } }
3. @AuthenticationPrincipal
Spring Security 3.2 부터는 annotation을 이용하여 현재 로그인한 사용자 객체를 인자에 주입할 수 있습니다.
만약 UserDetails 를 구현한 CustomUser 클래스가 있고, UserDetailsService 구현체에서 CustomUser 객체를 반환한다고 가정합시다.
(가장 흔한 케이스)
@Data public class CustomUser implements UserDetails { // ... }
@Service public class UserSerivce implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { CustomUser customUser = null; // ... DB 등을 통해 사용자 정보를 셋팅 return customUser; }
다음과 같이 @AuthenticationPrincipal를 이용하여 CustomUser 객체를 인자에 넘겨줄 수 있습니다.
@Controller public class SecurityController { @GetMapping("/messages/inbox") public ModelAndView currentUserName(@AuthenticationPrincipal CustomUser customUser) { String username = customUser.getUsername(); // .. find messages for this user and return them ... } }
출처: http://itstory.tk/entry/Spring-Security-현재-로그인한-사용자-정보-가져오기 [덕's IT Story]
* DB에서 정보 불러오기
테스트를 위해 다음과 같이 hobby 컬럼을 추가 했다.
alter table users add COLUMN hobby VARCHAR(30) ;
loginMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 --> <mapper namespace="net.macaronics.mapper.loginMapper"> <select id="getLoginUser" resultType="net.macaronics.web.domain.UserVO" > select * from users WHERE username =#{username} </select> </mapper>
UserVO
1. UserDetails 를 상속받고 있다는 점이 중요하다.
2. UserDetails 를 상속받는 정보를 저장하기 위해 다음과 같은 필드를 만든후 setter 를 메소드를 생성한다.
/** * <pre> * 1. 패키지명 : net.macaronics.web.domain * 2. 타입명 : UserVO.java * 3. 작성일 : 2017. 11. 24. 오후 7:29:45 * 4. 저자 : 최준호 * * </pre> * */ package net.macaronics.web.domain; import java.util.Collection; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class UserVO implements UserDetails { private String username; private String password; private String hobby; //UserDetails 를 상속받는 정보를 저장하기 위해 다음과 같은 필드를 만든후 setter 를 메소드를 생성한다. private Collection<? extends GrantedAuthority> authorities; private boolean isAccountNonExpired; private boolean isAccountNonLocked; private boolean isCredentialsNonExpired; private boolean isEnabled; public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO Auto-generated method stub return authorities; } @Override public String getPassword() { // TODO Auto-generated method stub return password; } @Override public String getUsername() { // TODO Auto-generated method stub return username; } @Override public boolean isAccountNonExpired() { // TODO Auto-generated method stub return isAccountNonExpired; } @Override public boolean isAccountNonLocked() { // TODO Auto-generated method stub return isAccountNonLocked; } @Override public boolean isCredentialsNonExpired() { // TODO Auto-generated method stub return isCredentialsNonExpired; } @Override public boolean isEnabled() { // TODO Auto-generated method stub return isEnabled; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setAuthorities(Collection<? extends GrantedAuthority> authorities) { this.authorities = authorities; } public void setAccountNonExpired(boolean isAccountNonExpired) { this.isAccountNonExpired = isAccountNonExpired; } public void setAccountNonLocked(boolean isAccountNonLocked) { this.isAccountNonLocked = isAccountNonLocked; } public void setCredentialsNonExpired(boolean isCredentialsNonExpired) { this.isCredentialsNonExpired = isCredentialsNonExpired; } public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } @Override public String toString() { return "UserVO [username=" + username + ", password=" + password + ", hobby=" + hobby + ", authorities=" + authorities + ", isAccountNonExpired=" + isAccountNonExpired + ", isAccountNonLocked=" + isAccountNonLocked + ", isCredentialsNonExpired=" + isCredentialsNonExpired + ", isEnabled=" + isEnabled + "]"; } }
UserDAO
/** * <pre> * 1. 패키지명 : net.macaronics.web.service * 2. 타입명 : UserSerivce.java * 3. 작성일 : 2017. 11. 24. 오후 7:28:15 * 4. 저자 : 최준호 * * </pre> * */ package net.macaronics.web.persistence; import net.macaronics.web.domain.UserVO; public interface UserDAO { public UserVO getLoginUser(String username) throws Exception; }
UserDAOImpl
/** * <pre> * 1. 패키지명 : net.macaronics.web.persistence * 2. 타입명 : UserDAOImpl.java * 3. 작성일 : 2017. 11. 25. 오후 1:53:53 * 4. 저자 : 최준호 * * </pre> * */ package net.macaronics.web.persistence; import org.apache.ibatis.session.SqlSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import net.macaronics.web.domain.UserVO; @Repository public class UserDAOImpl implements UserDAO { private static final Logger log = LoggerFactory.getLogger(UserDAOImpl.class); //private static final String namespace="net.macaronics.mapper.o.memberMapper."; private static final String namespace = "net.macaronics.mapper.loginMapper."; @Autowired private SqlSession sqlSession; @Override public UserVO getLoginUser(String username) throws Exception{ // TODO Auto-generated method stub return sqlSession.selectOne(namespace+"getLoginUser", username); } }
UserSerivce
3. DB 등을 통해 사용자 정보를 셋팅
/** * <pre> * 1. 패키지명 : net.macaronics.web.service * 2. 타입명 : UserSerivce.java * 3. 작성일 : 2017. 11. 24. 오후 7:28:15 * 4. 저자 : 최준호 * * </pre> * */ package net.macaronics.web.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import net.macaronics.web.domain.UserVO; import net.macaronics.web.persistence.UserDAO; @Service public class UserSerivce{ @Autowired private UserDAO userDAO; public UserVO loadUserByUsername(String username) throws Exception { UserVO userVO = null; // ... DB 등을 통해 사용자 정보를 셋팅 try { userVO=userDAO.getLoginUser(username); } catch (Exception e) { e.printStackTrace(); } return userVO; } }
SecurityController
Authentication 를 이용해서 시큐리티 UserDetails 클래스 정보를 가져온다.
이 정보의 아이디를 이용해서 로그인 한 유저정보를 불러올 수 있다.
DB 에서 불러온 유저 정보에 UserDetails 정보 저장 시킨다.
/** * <pre> * 1. 패키지명 : net.macaronics.web.controller * 2. 타입명 : SecurityController.java * 3. 작성일 : 2017. 11. 24. 오후 7:11:12 * 4. 저자 : 최준호 * * </pre> * */ package net.macaronics.web.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import net.macaronics.web.domain.UserVO; import net.macaronics.web.service.UserSerivce; @Controller public class SecurityController { private static Logger logger =LoggerFactory.getLogger(SecurityController.class); @Autowired private UserSerivce userService; @GetMapping("/loginInfo") public String currentUserName(Authentication authentication, Model model) throws Exception{ if(authentication !=null){ logger.info("userVO.getUsername() {} : " , authentication.getName()); //시큐리티에서 UserDetails 이용하여 로그인 정보를 불러온다. UserDetails userDetails = (UserDetails) authentication.getPrincipal(); // 아이디를 이용하여 DB 에서 정보를 불러 온다. UserVO userVO=userService.loadUserByUsername(authentication.getName()); // DB 에서 불러온 정보를 userVO 객체에 저장한다. //권한 저장 및 UserDetails 정보 저장 userVO.setAuthorities(userDetails.getAuthorities()); userVO.setAccountNonExpired(userDetails.isAccountNonExpired()); userVO.setAccountNonLocked(userDetails.isAccountNonLocked()); userVO.setCredentialsNonExpired(userDetails.isCredentialsNonExpired()); userVO.setEnabled(userDetails.isEnabled()); logger.info(" userDetails 출력 : {} " , userDetails.toString()); logger.info("출력 : {} " , userVO.toString()); model.addAttribute("userVO", userVO); } return "home"; } }
home.jsp
<%@page import="net.macaronics.web.domain.UserVO"%> <%@page import="org.springframework.security.core.userdetails.User"%> <%@page import="org.springframework.security.core.context.SecurityContextHolder"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ page session="false"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> <html> <head> <title>Home</title> </head> <body> <h1>Home!</h1> <sec:authorize access="isAnonymous()"> <p> <a href="<c:url value="/login/loginForm.do" />">로그인</a> </p> </sec:authorize> <sec:authorize access="isAuthenticated()"> <form:form action="${pageContext.request.contextPath}/logout" method="POST"> <input type="submit" value="로그아웃" /> </form:form> </sec:authorize> <h3> [<a href="<c:url value="/user/introduction.do" />">소개 페이지</a>] [<a href="<c:url value="/admin/adminHome.do" />">관리자 홈</a>] </h3> <sec:authorize access="isAuthenticated()"> <% User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if(user!=null){ System.out.println("username = " + user.toString()); out.write(user.getUsername()); } %> </sec:authorize> <c:if test="${ not empty userVO }"> <br> <h2>로그인한 정보 DB에서 개별 상세 정보 가져오기</h2> ${userVO.toString() } </c:if> </body> </html>
다음과 같이 로그한 유저의 개인정보들을 출력시켜 줄 수 있다.
또한, 이러한 정보를 세션에 저장 시켜 유지 시켜 줘도 될 것이다.
다음은 콘솔에 찍힌 출력물이다.
INFO : net.macaronics.web.controller.SecurityController - userDetails 출력 : org.springframework.security.core.userdetails.User@6a68dc7: Username: user2; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
INFO : net.macaronics.web.controller.SecurityController - 출력 : UserVO [username=user2, password=1, hobby=등산, authorities=[ROLE_USER], isAccountNonExpired=true, isAccountNonLocked=true, isCredentialsNonExpired=true, isEnabled=true]
username = org.springframework.security.core.userdetails.User@6a68dc7: Username: user2; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
정상적으로 작동한다면 http://localhost:5050/loginInfo 주소에서 다음 이미와 같은 정보를 볼 수 있을 것이다.
댓글 ( 4)
댓글 남기기