'session'에 해당되는 글 1건

  1. 2015.12.30 :: Spring Security를 이용한 login(1) 1
개발관련/Spring 2015. 12. 30. 16:13

Spring Security는 기존 mvc model2에서 일일히 login 관련 한 것들을 알아서 해준다. 예를들어 암호화(hash) 비교, session 체크(cookie 포함), 이중 로그인 체크등 이에 따른 예외 처리등을 관리해준다. 물론 더 많은 기능이 있겠지만, 여기서는 로그인 관련한 것들을 알아보자. 


spring 4.x를 사용하였으며 derby db를 사용했으며 jpa를 사용하며 간단히 만든다. 

먼저 pom 파일에 security관련 lib를 추가하자. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- Spring Security -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
cs


그리고 web.xml에 security관련 context.xml과 security에 필요한 Filter를 추가한다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml, /WEB-INF/spring/security-context.xml</param-value>
    </context-param>    
....
 <!--생략 ---->
...
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
 
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
cs


그럼 이제 context.xml을 설정해보자. 먼저 jpa관련 설정을 먼저한다. jpa 관련 설정은 root_context.xml에 설정한다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
 
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
 
        <property name="driverClass" value="org.apache.derby.jdbc.ClientDriver"></property>
        <property name="username" value="user"></property>
        <property name="password" value="gnogun"></property>
        <property name="url"
            value="jdbc:derby://localhost:1527/txtest"></property>
    </bean>
 
    <jpa:repositories base-package="com.gno.sample.repository"
        entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
 
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
 
        <property name="dataSource" ref="dataSource" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
                <prop key="hibernate.default_schema">txtest2</prop>
                <prop key="hibernate.connection.pool_size">1</prop>
                <prop key="hibernate.connection.shutdown">true</prop>
                <prop key="hibernate.show_sql">true</prop<!-- SQL 출력 -->
                <prop key="hibernate.ddl_auto">auto</prop>
 
                <!-- 
                <prop key="hibernate.hbm2ddl.auto">create</prop> 
                 -->
 
 
            </props>
        </property>
        <property name="packagesToScan" value="com.gno.sample.dto" />
    </bean>
 
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
 
    </bean>
 
 
 
    
</beans>
 
cs


jpa 설정은 찾아보기로 하고 여기에선 스킵한다. 


servlet-context.xml 을 설정한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
    
    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />
 
    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />
 
    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
    
    <context:component-scan base-package="com.gno.sample" />
    
    <interceptors>
            <beans:bean class="com.gno.sample.security.CustomInterceptor" />
    </interceptors>
    
    
</beans:beans>
 
cs


interceptors 라는 tag가 있는데 controller에서 오는 값을 aop처럼  관리 하지만 security에선 session에 관한 권한 및 session을 관리한다. 이후 소스 부분에 다시 설명하겠다. 


이제 security-context.xml를 보자. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <context:annotation-config></context:annotation-config>
 
 
<!--  user-expressions 는 intercept-url태그의 access가 true일 경우  
    정의된 함수 ( hasAnyRole, isAnonymous() 등등 ) 를 사용할 수 있게 해준다.  -->
 
    <security:http auto-config='true' use-expressions="true">
<!-- spring 4.x때 추가된 옵션으로 ssl을 사용하지 않을 경우 csrf는 disalbed true로 해준다.  -->
        <security:csrf disabled="true" />
        <!-- autoconfig=false 면? filter도 -->
        <!-- <security:intercept-url pattern="/login" access="isAnonymous()" /> -->
 
 
        <!-- access 이름들은 prefix가 정해져 있음 (default값 ROLE_ ) 재정의 하는 방법은 찾아놨는데 이름을 
            뭘 붙일지 몰라서 그냥 default prefix 사용했음 -->
 
        <security:intercept-url pattern="/admin.do"
            access="hasAnyRole('ROLE_ADMIN')" />
 
        <security:intercept-url pattern="/main.do"
            access="hasAnyRole('ROLE_ADMIN', 'ROLE_USER')" />
 
        
 
        <security:intercept-url pattern="/user.do"
            access="hasAnyRole('ROLE_ADMIN', 'ROLE_USER')" />
 
        <security:intercept-url pattern="/*" access="permitAll" />
        <!-- access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" -->
 
        <!-- <security:anonymous /> 
        <security:x509/> 
        <security:http-basic /> 
        <security:session-management></security:session-management> 
            <security:expression-handler ref=""/> 
            <security:custom-filter ref=""/> 
            <security:port-mappings></security:port-mappings> 
            <security:request-cache ref=""/> 
            <security:remember-me/> -->
 
        <!-- always-use-default-target='true' = 서버가 죽었다 살아났을때 기존 가려고 했던 페이지를 무시하고 
            무조건 handler에 정의된 페이지로 이동 -->
<!--authentication-failure-handler-ref와 authentication-success-handler-ref를 사용하지 않을경우는 
authentication-failure-url속성을 사용하여 리다이렉트를 해준다.
  -->
        <security:form-login login-page="/login.do"
            default-target-url="/main.do" authentication-success-handler-ref="loginSuccessHandler"
            authentication-failure-handler-ref="loginFailureHandler"
            always-use-default-target="true" login-processing-url="/loginProcess"
            username-parameter="username" password-parameter="password" />
        <!-- authentication-failure-url="/login" login-processing-url="" password-parameter="" 
            username-parameter="" -->
 
 
 
        <security:logout logout-url="/logout"
            invalidate-session="true"
            success-handler-ref="logoutSuccessHandler" 
            />
 
        <!--
            delete-cookies="JSESSIONID,auth" 
            logout-success-url="/login.do" />
         delete-cookies="" logout-url="" invalidate-session="true" success-handler-ref="" -->
 
    </security:http>
 
    <security:authentication-manager>
 
        <!-- <security:authentication-provider ref="userProvider"> </security:authentication-provider> -->
        <security:authentication-provider
            ref="CustomAuthenticationProvider">
        </security:authentication-provider>
 
 
    </security:authentication-manager>
<!--
    provider는 이미 form에서 id 및 pwd(암호화 된값)을 가져오고 db에서 가져온 값을 UserService를 통해  UserDetail을 저장을 하며
    UserDetail은 인증정보(db에서 가져온 사용자 값) 과 권한정보를 
    가져와서 provider는 먼저 인증을 비교한후  true가 되면 권한(Grant Authority)을 부여한다.      
      
 -->
    <bean id="CustomAuthenticationProvider" class="com.gno.sample.security.CustomAuthenticationProvider">
        <property name="userDetailsService" ref="userService"></property>
        <property name="passwordEncoder" ref="passwordEncoder"></property>
    </bean>
<!-- UserDeatilService(com.gno.sample.security.CustomUserDetailService) 클래스는 
    인증(authentication)에 사용할 UserDetails 객체를 생성하는 작업이고 , 
    UserDetails는 db에서 id값으로 user의 정보 및  권한(authority)정보를  저장한다.   
    이상 스러운건 이미 암호화 값으로 변경이 되있다.  -->
    <bean id="userService" class="com.gno.sample.security.CustomUserDetailService" />
 
    <bean id="passwordEncoder"
        class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
 
    <bean id="loginSuccessHandler" class="com.gno.sample.security.LoginSuccessHandler"></bean>
    <bean id="loginFailureHandler" class="com.gno.sample.security.LoginFailureHandler"></bean>
    <bean id="logoutSuccessHandler" class="com.gno.sample.security.CustomLogoutSuccessHandler"></bean>
</beans>
 
cs

굉장히 많은거 같은데 한개씩 보도록 하자. 

먼저 security:http  태그를 살펴보자. 속성으로 auto-config라는 넘이 있다 

이넘이 true 일경우 filter는 defalut 값으로 되며 만약 false라면 

<security:anonymous /> 

<security:x509/> 

<security:http-basic /> 

<security:session-management></security:session-management> 

<security:expression-handler ref=""/> 

<security:custom-filter ref=""/> 

<security:port-mappings></security:port-mappings> 

<security:request-cache ref=""/> 

<security:remember-me/> 

위의 filter들을 셋팅해줘야 한다. 


다음 속성으로 user-expressions 라는 속성은 만약 false라면 spring에서 제공하는 hasAnyRole,isAnonymous() 등 내부 함수를 사용하지 못한다.  하지만 보통은 사용하므로 true로 해준다. 


csrf는 spring4.x에 추가된 태그로 ssl등을 사용하지 않을때는 disabled=true로 설정을 해준다. 


다음은 interceptor-url 이다. 이 태그는 각 url별 권한( autority)를 부여해준다. 그리고 높은 권한일 경우 먼저 써주고 낮은 권한 일 경우 아래에 써주는 것을 권장 하고 있다. 위의 설정 파일을 보면 ADMIN 권한 먼저 그다음 ADMIN과 User 그다음은 permitAll 로 주고 있다. 만약 순서가 잘못되면 권한 문제로 페이지가 잘못 나올경우 있으니 주의 하자. 


그리고 login과 logout은 각 handler가 존재한다. 먼저 login을 살펴보자. 


속성 

설명 

 login-page

로그인 page

 default-target-url

로그인 성공시 이동할 url 설정 

 authentication-success-handler-ref

로그인 성공시에 대한 프로세스 정의 보통 권한이 많을 경우 이 핸들러에서 redirect로 설정하며, defalut-target-url은 사용하지 않는다. 

 authentication-failure-handler-ref

로그인 실패시에 대한 프로세스 정의  

 always-use-default-target

WAS 서버가 죽었다  살아 났을때 기존 가려고 했던 페이지는 무시하고 무조건 핸들러에 정의된 페이지로 이동

 login-processing-url

 로그인 처리에 대한 url 어떠한 controller 든지 이런것은 정의 되지 않지만 로그인 form 내에서 action url은 이 url로 정의 되야 하며 내부적으로 이 url로 로그인 processing이 진행된다. 

 username-parameter, password-parameter

만약 이 파라미터가 없다면 스프링에서 제공되는 j_username, j_password를 사용해야한다.  


logout은 invalidate-session의 경우 logout이 진행되면 session 정보를 설정값에 따라 삭제를 진행한다. true일경우 삭제. 


암호화 방식은 bean으로 설정을 하며, 위의 설정 파일에는 id는 passwordEncoder이며 암호화 방식 bcrypt를 사용한다. 


자 이제 마지막으로 3가지가 등장한다. AuthenticationProvider, UserService, UserDetail 이 존재한다. 

provider는 이미 form에서 id 및 pwd(암호화된 값)과 db의 값을 비교한후 true이며 권한(Grant Autority)를 부여한다. 이때 비교를 하기위해 참조값으로 암호화 방식의 bean을 등록해야한다. 

UserService는 인증(authentication)에 사용할 UserDetail 객체를 생성한다. 

UserDeatil은 user의 정보 및 권한 정보를 저장한다.


여기까지 설정은 모두 끝났다. 다음 글에서 소스를 살펴보자.











posted by 제스트
: