Spring Security를 사용할 때 빈에서 현재 사용자 이름(Security Context 등) 정보를 얻는 적절한 방법은 무엇입니까?
Spring Security를 사용하는 Spring MVC 웹 앱이 있습니다.현재 로그인한 사용자의 사용자 이름을 알고 싶습니다.아래 코드 스니펫을 사용하고 있습니다.이게 통용되는 방법인가요?
스프링 IMHO의 전체 목적을 저버린 컨트롤러 내부의 정적 메서드를 호출하는 것은 좋아하지 않습니다.현재 Security Context 또는 현재 인증을 대신 주입하도록 앱을 구성하는 방법이 있습니까?
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request...) {
final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
...
}
Spring 3을 사용하는 경우 가장 쉬운 방법은 다음과 같습니다.
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request, Principal principal) {
final String currentUser = principal.getName();
}
이 질문에 답한 이후 봄의 세계에는 많은 것이 바뀌었다.Spring은 현재 사용자를 컨트롤러로 이동시키는 것을 단순화했습니다.다른 콩의 경우, Spring은 저자의 제안을 받아들여 SecurityContextHolder의 주입을 간소화했다.자세한 내용은 댓글에 있습니다.
을 사용하다「 」를 하는 대신에, 「 」를 사용합니다.SecurityContextHolder
내 컨트롤러에, 나는 어떤 것을 주입하고 싶다.SecurityContextHolder
이렇게할 수 나 것 외에 .
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
컨트롤러(또는 POJO)는 다음과 같습니다.
public class FooController {
private final SecurityContextFacade securityContextFacade;
public FooController(SecurityContextFacade securityContextFacade) {
this.securityContextFacade = securityContextFacade;
}
public void doSomething(){
SecurityContext context = securityContextFacade.getContext();
// do something w/ context
}
}
또, 인터페이스가 디커플링의 포인트이기 때문에, 유닛의 테스트는 간단합니다.이 예에서는 Mockito를 사용합니다.
public class FooControllerTest {
private FooController controller;
private SecurityContextFacade mockSecurityContextFacade;
private SecurityContext mockSecurityContext;
@Before
public void setUp() throws Exception {
mockSecurityContextFacade = mock(SecurityContextFacade.class);
mockSecurityContext = mock(SecurityContext.class);
stub(mockSecurityContextFacade.getContext()).toReturn(mockSecurityContext);
controller = new FooController(mockSecurityContextFacade);
}
@Test
public void testDoSomething() {
controller.doSomething();
verify(mockSecurityContextFacade).getContext();
}
}
인터페이스의 디폴트 실장은 다음과 같습니다.
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
마지막으로 실제 가동 중인 Spring 구성은 다음과 같습니다.
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
모든 것의 의존성 주입 용기인 스프링이 비슷한 것을 주입하는 방법을 제공하지 않았다는 것은 조금 바보같지 않다. 알겠습니다.SecurityContextHolder
아체기에게서 물려받았지만, 여전히.한 것은,은 아주이다.SecurityContextHolder
에 있는 을 얻기 .SecurityContextHolderStrategy
instance(인터페이스)를 삽입할 수 있습니다.사실 나는 그런 취지의 지라호까지 개설했다.
마지막으로 한 가지 말씀 드리자면, 저는 방금 전에 여기서 했던 답변을 상당히 바꿨습니다.금금면하지만 동료가 지적한 것처럼 멀티 스레드 환경에서는 이전 답변이 작동하지 않습니다.가 되는 " " " 。SecurityContextHolderStrategy
SecurityContextHolder
로는 'ThreadLocalSecurityContextHolderStrategy
「」를 격납합니다.SecurityContext
a s in a 、 a 、 a 습있다 。ThreadLocal
때문에 꼭 , 사투리 등을 SecurityContext
시 할 수 있습니다. - 화화 directly directly the - the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the에서 할수.ThreadLocal
멀티스레드 환경에서는 매번 올바른 것을 얻을 수 있습니다.
현재 사용자에 대해 Security Context를 조회해야 하는 것은 이 문제를 처리하는 매우 비원칙적인 방법인 것 같습니다.
저는 이 문제에 대처하기 위해 정적 "도움말" 클래스를 작성했습니다.이것은 글로벌하고 정적인 방법이라는 점에서 지저분하지만, 보안과 관련된 내용을 변경할 경우 최소한 한 곳에서만 세부 사항을 변경할 필요가 있다고 생각했습니다.
/**
* Returns the domain User object for the currently logged in user, or null
* if no User is logged in.
*
* @return User object for the currently logged in user, or null if no User
* is logged in.
*/
public static User getCurrentUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal()
if (principal instanceof MyUserDetails) return ((MyUserDetails) principal).getUser();
// principal object is either null or represents anonymous user -
// neither of which our domain User object can represent - so return null
return null;
}
/**
* Utility method to determine if the current user is logged in /
* authenticated.
* <p>
* Equivalent of calling:
* <p>
* <code>getCurrentUser() != null</code>
*
* @return if user is logged in
*/
public static boolean isLoggedIn() {
return getCurrentUser() != null;
}
JSP 페이지에 표시되도록 하려면 Spring Security Tag Lib를 사용합니다.
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
다음 중 하나의 태그를 사용하려면 JSP에서 security taglib를 선언해야 합니다.
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
그런 다음 jsp 페이지에서 다음과 같이 수행합니다.
<security:authorize access="isAuthenticated()">
logged in as <security:authentication property="principal.username" />
</security:authorize>
<security:authorize access="! isAuthenticated()">
not logged in
</security:authorize>
메모: @SBerg413의 코멘트에서 설명한 바와 같이,
use-priptions="true"
security.xml Configuration의 "security" 태그로 이동합니다.
ver 3.는, Spring Security ver > = 3.2 를 할 수 .@AuthenticationPrincipal
★★★★
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(@AuthenticationPrincipal CustomUser currentUser, HttpServletRequest request) {
String currentUsername = currentUser.getUsername();
// ...
}
서서,,CustomUser
를 구현하는 커스텀오브젝트입니다UserDetails
에 UserDetailsService
.
자세한 내용은 Spring Security 레퍼런스 문서의 @AuthenticationPrincipal 장을 참조하십시오.
Http ServletRequest.getUserPrincipal()에 의해 인증된 사용자를 취득한다.
예:
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.support.RequestContext;
import foo.Form;
@Controller
@RequestMapping(value="/welcome")
public class IndexController {
@RequestMapping(method=RequestMethod.GET)
public String getCreateForm(Model model, HttpServletRequest request) {
if(request.getUserPrincipal() != null) {
String loginName = request.getUserPrincipal().getName();
System.out.println("loginName : " + loginName );
}
model.addAttribute("form", new Form());
return "welcome";
}
}
Spring 3+에는 다음과 같은 옵션이 있습니다.
옵션 1:
@RequestMapping(method = RequestMethod.GET)
public String currentUserNameByPrincipal(Principal principal) {
return principal.getName();
}
옵션 2:
@RequestMapping(method = RequestMethod.GET)
public String currentUserNameByAuthentication(Authentication authentication) {
return authentication.getName();
}
옵션 3:
@RequestMapping(method = RequestMethod.GET)
public String currentUserByHTTPRequest(HttpServletRequest request) {
return request.getUserPrincipal().getName();
}
옵션 4 : Fancy one :자세한 것은 이쪽을 참조해 주세요.
public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
...
}
난 그냥 이렇게 할 거야:
request.getRemoteUser();
네, 일반적으로 통계는 좋지 않습니다.그러나 이 경우 static이 가장 안전한 코드입니다.안전한 코드입니다.보안 콘텍스트는 프린서펄과 현재 실행 중인 스레드를 관련짓기 때문에 가장 안전한 코드는 가능한 한 스레드에서 직접 스태틱에 액세스합니다.주입된 래퍼 클래스 뒤에 액세스를 숨기면 공격자가 더 많은 공격 지점을 얻을 수 있습니다.코드 액세스(jar가 서명된 경우 변경에 어려움을 겪을 수 있음)는 필요하지 않습니다. 실행 시 또는 일부 XML을 클래스 경로에 슬립할 수 있는 구성을 재정의하는 방법만 필요합니다.서명된 코드에 주석 주입을 사용하는 경우에도 외부 XML로 재정의할 수 있습니다. 이러한 XML은 실행 중인 시스템에 악성 주체를 주입할 수 있습니다.이것이 아마도 봄이 이 경우에 봄답지 않은 일을 하는 이유일 것이다.
지난번 작성한 봄 MVC 앱에서는 Security Context 홀더를 삽입하지 않았습니다만, 이것과 관련된 두 가지 유틸리티 메서드가 있는 베이스 컨트롤러가 있었습니다.isAuthenticated()와 getUsername()입니다.내부에서는 사용자가 설명한 스태틱메서드 콜이 실행됩니다.
적어도 나중에 리팩터가 필요할 때는 한 곳에 두어라.
Spring AOP 어프로치를 사용할 수 있습니다.예를 들어, 서비스가 있는 경우 현재 주체를 알아야 합니다.@Principal과 같은 커스텀 주석을 도입할 수 있습니다.이것은 본 서비스가 주된 의존관계에 있는 것을 나타냅니다.
public class SomeService {
private String principal;
@Principal
public setPrincipal(String principal){
this.principal=principal;
}
}
그런 다음 Method Before Advise를 확장해야 한다고 생각되는 조언에서 특정 서비스에 @Principal 주석이 있는지 확인하고 Principal name을 주입하거나 대신 'ANNONYUS'로 설정하십시오.
유일한 문제는 Spring Security에서 인증한 후에도 컨테이너에 사용자/프린서펄 빈이 존재하지 않기 때문에 의존관계 주입이 어렵다는 것입니다.Spring Security를 사용하기 전에 현재 프린서펄을 가진 세션 스코프 빈을 생성하여 이를 "Auth Service"에 삽입하고 해당 서비스를 어플리케이션 내의 다른 대부분의 서비스에 주입합니다.따라서 이러한 서비스는 단순히 authService.getCurrentUser()를 호출하여 객체를 가져옵니다.코드에 세션에서 동일한 주체에 대한 참조가 있는 경우 세션 범위 콩의 속성으로 설정할 수 있습니다.
Spring 3을 사용하고 있으며 컨트롤러에서 인증된 주체가 필요한 경우 다음과 같은 작업을 수행하는 것이 가장 좋습니다.
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@Controller
public class KnoteController {
@RequestMapping(method = RequestMethod.GET)
public java.lang.String list(Model uiModel, UsernamePasswordAuthenticationToken authToken) {
if (authToken instanceof UsernamePasswordAuthenticationToken) {
user = (User) authToken.getPrincipal();
}
...
}
이거 드셔보세요
인증 = SecurityContextHolder.getContext().getAuthentication();
문자열 userName = authentication.getName();
사용하고 있는 것은@AuthenticationPrincipal
의 주석.@Controller
수업뿐만 아니라@ControllerAdvicer
주석을 달았다.예:
@ControllerAdvice
public class ControllerAdvicer
{
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAdvicer.class);
@ModelAttribute("userActive")
public UserActive currentUser(@AuthenticationPrincipal UserActive currentUser)
{
return currentUser;
}
}
어디에UserActive
로그 사용자 서비스에 사용하는 클래스로, 에서 확장됩니다.org.springframework.security.core.userdetails.User
. 예를 들면:
public class UserActive extends org.springframework.security.core.userdetails.User
{
private final User user;
public UserActive(User user)
{
super(user.getUsername(), user.getPasswordHash(), user.getGrantedAuthorities());
this.user = user;
}
//More functions
}
정말 쉬워요.
정의Principal
spring은 호출 시 현재 인증된 사용자를 메서드에 주입합니다.
프리마커 페이지에서 사용자 세부사항을 지원하는 방법을 공유합니다.모든 것이 매우 간단하고 완벽하게 작동합니다!
를 하다에 됩니다.default-target-url
(장표 작성 후 페이지 수)이 페이지의 Controler 메서드는 다음과 같습니다.
@RequestMapping(value = "/monitoring", method = RequestMethod.GET)
public ModelAndView getMonitoringPage(Model model, final HttpServletRequest request) {
showRequestLog("monitoring");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String userName = authentication.getName();
//create a new session
HttpSession session = request.getSession(true);
session.setAttribute("username", userName);
return new ModelAndView(catalogPath + "monitoring");
}
제 FTL 코드는 다음과 같습니다.
<@security.authorize ifAnyGranted="ROLE_ADMIN, ROLE_USER">
<p style="padding-right: 20px;">Logged in as ${username!"Anonymous" }</p>
</@security.authorize>
이것으로 승인 후 모든 페이지에 사용자 이름이 표시됩니다.
언급URL : https://stackoverflow.com/questions/248562/when-using-spring-security-what-is-the-proper-way-to-obtain-current-username-i
'programing' 카테고리의 다른 글
파일 설명자에서 파일 포인터를 가져오려면 어떻게 해야 합니까? (0) | 2022.08.07 |
---|---|
Vuex에서 상태 속성을 기준으로 계산하는 올바른 방법은 무엇입니까? (0) | 2022.08.07 |
Java의 String을 사용해야 합니까?퍼포먼스가 중요한 경우 format(). (0) | 2022.08.07 |
Vue 2에서 동적 클래스 이름과 조건부 클래스 이름 혼합 (0) | 2022.08.07 |
Laravel에서 vue-filemanager 프런트엔드를 커스터마이즈하는 방법 (0) | 2022.08.07 |