복붙노트

[SPRING] 스프링 보안 : 맞춤형 사용자 상세 정보

SPRING

스프링 보안 : 맞춤형 사용자 상세 정보

저는 Java와 Spring 3 (PHP는 지난 8 년 동안 주로 사용되었습니다)에 익숙하지 않았습니다. 모든 기본 userDetails 및 userDetailsService와 함께 작동하는 스프링 보안 3을 얻었으며 다음을 사용하여 컨트롤러에서 로그인 된 사용자의 사용자 이름에 액세스 할 수 있다는 것을 알고 있습니다.

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName(); //get logged in username

하지만 알아낼 수없는 두 가지 문제가 있습니다.

Security-applicationContext.xml :

<global-method-security secured-annotations="enabled"></global-method-security>     
<http auto-config='true' access-denied-page="/access-denied.html">
    <!-- NO RESTRICTIONS -->        
    <intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <intercept-url pattern="/*.html" access="IS_AUTHENTICATED_ANONYMOUSLY"  /> 
    <!-- RESTRICTED PAGES -->
    <intercept-url pattern="/admin/*.html" access="ROLE_ADMIN" />
    <intercept-url pattern="/member/*.html" access="ROLE_ADMIN, ROLE_STAFF" />

    <form-login login-page="/login.html"
                login-processing-url="/loginProcess"
                authentication-failure-url="/login.html?login_error=1"
                default-target-url="/member/home.html" />
    <logout logout-success-url="/login.html"/>
</http>

<authentication-manager>
    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource" authorities-by-username-query="SELECT U.username, UR.authority, U.userid FROM users U, userroles UR WHERE U.username=? AND U.roleid=UR.roleid LIMIT 1" />
        <password-encoder hash="md5"/>
    </authentication-provider>
</authentication-manager>

login.jsp :

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<tiles:insertDefinition name="header" />
<tiles:insertDefinition name="menu" />
<tiles:insertDefinition name="prebody" />

<h1>Login</h1>

<c:if test="${not empty param.login_error}">
    <font color="red"><c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.<br /><br /></font>
</c:if>
<form name="f" action="<c:url value='/loginProcess'/>" method="POST">
    <table>
        <tr><td>User:</td><td><input type='text' name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>' /></td></tr>
            <tr><td>Password:</td><td><input type='password' name='j_password' /></td></tr>
            <tr><td>&nbsp;</td><td><input type="checkbox" name="_spring_security_remember_me" /> Remember Me</td></tr>
            <tr><td>&nbsp;</td><td><input name="submit" type="submit" value="Login" /></td></tr>
        </table>
    </form>

<tiles:insertDefinition name="postbody" />
<tiles:insertDefinition name="footer" />

해결법

  1. ==============================

    1.이 질문에 끔찍한 일이 많이 있습니다. 나는 그것을 조각으로 다루려고 노력할 것이다 ...

    이 질문에 끔찍한 일이 많이 있습니다. 나는 그것을 조각으로 다루려고 노력할 것이다 ...

    Q # 1 : 가능한 접근법이 몇 가지 있습니다.

    접근법 # 1 : UserDetails 객체에 추가하려는 다른 속성이있는 경우 해당 속성을 포함하는 UserDetails 인터페이스의 대체 구현을 해당 getter 및 setter와 함께 제공해야합니다. 이렇게하려면 UserDetailsService 인터페이스의 대체 구현을 제공해야합니다. 이 구성 요소는 기본 데이터 저장소에 이러한 추가 특성을 유지하는 방법을 이해해야하거나 해당 데이터 저장소에서 읽을 때 추가 특성을 채우는 방법을 이해해야합니다. 이 모든 것을 이렇게 연결합니다.

    <beans:bean id="userDetailsService" class="com.example.MyCustomeUserDetailsService">
    <!-- ... -->
    </beans:bean>
    
    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="authenticationProvider"/>
    </authentication-manager>
    
    <beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <beans:property name="userDetailsService" ref="userDetailsService"/>
    </beans:bean>
    

    Approaches # 2 : 나처럼 스프링 보안에 특정한 사용자 / 계정 세부 사항과는 별도로 도메인 특정 사용자 / 계정 세부 사항을 유지하는 것이 더 낫다는 것을 발견 할 수있다. 이것은 당신을 위해 또는 그렇지 않을 수도 있습니다. 그러나이 접근법에서 어떤 지혜를 찾을 수 있다면 현재 가지고있는 설정을 고수하고 추가 사용자 / 계정 도메인 객체, 해당 저장소 / DAO 등을 추가 할 것입니다. 도메인 별 사용자 / 계정을 만들려면 다음과 같이하십시오.

    User user = userDao.getByUsername(SecurityContextHolder.getContext().getAuthentication().getName());
    

    Q # 2 : Spring Security는 자동으로 UserDetails를 세션에 저장합니다 (명시 적으로 해당 동작을 재정의하지 않은 경우 제외). 따라서 각 컨트롤러 메소드에서 직접이 작업을 수행 할 필요는 없습니다. 처리중인 SecurityContextHolder 객체는 실제로 모든 요청이 시작될 때 Authentication 객체, UserDetails 등을 포함하여 SecurityContext로 (SS별로) 채워집니다. 이 컨텍스트는 각 요청이 끝날 때 지워지지만 데이터는 항상 세션에 남아 있습니다.

    그러나 Spring MVC 컨트롤러에서 HttpServletRequest, HttpSession 객체 등을 다루는 것이 실제로는 좋은 습관은 아니라는 것을주의해야한다. Spring은 거의 항상 그 일을 할 필요없이 더 깨끗하고 더 관용적 인 방법을 제공합니다. 그 장점은 컨트롤러 메소드의 서명과 로직이 유닛 테스트 (예 : HttpSession)에서 모의하기가 어렵고 자신의 도메인 객체 (또는 해당 도메인 객체의 스텁 / 모의 객체)에 의존하지 않고 중단된다는 것입니다 ). 이렇게하면 컨트롤러의 테스트 가능성이 크게 높아져 실제로 컨트롤러를 테스트 할 가능성이 높아집니다. :)

    희망이 좀 도움이됩니다.

  2. ==============================

    2.내 생각에, 사용자 정의 UserDetails 구현은 훌륭하지만 사용자의 불변 특성에만 사용해야합니다.

    내 생각에, 사용자 정의 UserDetails 구현은 훌륭하지만 사용자의 불변 특성에만 사용해야합니다.

    사용자 정의 사용자 객체가 UserDetails를 재정의하면 쉽게 변경되지 않습니다. 수정 된 세부 정보로 완전히 새로운 인증 개체를 만들어야하며 수정 된 UserDetails 개체를 보안 컨텍스트에 다시 붙일 수 없습니다.

    내가 구현하고있는 응용 프로그램에서 나는 이것을 깨닫고 그것을 재 설계해야만합니다. 그래서 모든 요청에 ​​따라 변경되는 사용자에 대한 성공적인 인증 세부 사항 (모든 페이지로드시 DB에서 다시로드하지 않으려 고합니다) 세션에 별도로 보관해야하지만 인증 확인 후에는 액세스 가능 / 변경 가능해야합니다.

    https://stackoverflow.com/a/8769670/1411545에서 언급 한이 WebArgumentResolver가 내 상황에 대한 더 나은 솔루션인지 파악하려고합니다.

  3. ==============================

    3.세션에 직접 액세스하는 것은 약간 지저분하고 오류가 발생할 수 있습니다. 예를 들어, 사용자가 remember-me 또는 리디렉션을 포함하지 않는 다른 메커니즘을 사용하여 인증 된 경우 해당 요청이 완료 될 때까지 세션이 채워지지 않습니다.

    세션에 직접 액세스하는 것은 약간 지저분하고 오류가 발생할 수 있습니다. 예를 들어, 사용자가 remember-me 또는 리디렉션을 포함하지 않는 다른 메커니즘을 사용하여 인증 된 경우 해당 요청이 완료 될 때까지 세션이 채워지지 않습니다.

    사용자 지정 액세서 인터페이스를 사용하여 SecurityContextHolder에 대한 호출을 래핑합니다. 이 관련된 질문에 대한 내 대답을 참조하십시오.

  4. from https://stackoverflow.com/questions/10607696/spring-security-custom-userdetails by cc-by-sa and MIT license