서버 에러 로그

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'or****': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.***.common.service.***] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}

 

 

 

 

 

 

해결 방법

 

<context:component-scan base-package="egovframework,추가할패키지">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

spring 인터셉터 기능 정리

Web/Spring 2016. 6. 24. 10:02 Posted by Request

ApplicationInterceptor 와 WebContentInterceptor 메모

 

    <mvc:interceptors>
  <!-- 공통 인터셉터 -->
  <mvc:interceptor>
   <mvc:mapping path="/**" />
   <mvc:exclude-mapping path="/resources/**"/>
   <bean class="com.ezwel.core.framework.web.interceptor.ApplicationInterceptor" />
  </mvc:interceptor>


   <mvc:interceptor>
   <mvc:mapping path="/order/**" />
   <mvc:exclude-mapping path="/resources/**"/>
   <bean class="org.springframework.web.servlet.mvc.WebContentInterceptor" >
    <property name="cacheSeconds" value="0" />
    <property name="useExpiresHeader" value="true" />
    <property name="useCacheControlHeader" value="true" />
    <property name="useCacheControlNoStore" value="true" />
   </bean>
  </mvc:interceptor>
 </mvc:interceptors>

Spring Security를 이용한 login 이해

Web/Spring 2016. 3. 10. 13:41 Posted by Request

 

출처 : http://zest133.tistory.com/entry/Spring-Security%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-login1

Spring Security를 이용한 login(1)

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의 정보 및 권한 정보를 저장한다.


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

 

[출처 : http://zgundam.tistory.com/m/post/51]


이 화면은 로그인 화면에서 로그인이 정상적으로 이루어진 뒤에 자동으로 메인화면으로 이동했을때 로그인 폼이 그대로 나타나고 있기 때문에 로그인이 정상적으로 되었는지 알 수가 없다. 이 부분을 이제 바꾸어보도록 하겠다.


1. Custom Tag를 등록한다.

-Spring Security에서는 jsp 페이지에서 사용할 수 있는 Custom Tag를 제공한다.


<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>


=> 공통 include 파일이든 또는 Spring Security의 기능을 jsp 페이지에서 써야 하는 곳에서든 저 코드를 넣어서 Spring Security가 제공하는 tag를 사용할 수 있게해준다.


 먼저 Spring Security에서 로그인 정보를 가져오는 방법에 대해 설명하도록 하겠다. 2가지의 방법이 있는데 Spring Security가 자체적으로 제공하는 방법과 HttpServletRequest 객체에서 가져오는 방법이렇게 2가지가 있다.


Servlet Spec 2.5에서는 인증 과정을 마치고 난 뒤의 로그인 정보를 가져오는 기능만 있었으나 Servlet Spec 3.0 이상에서는 Spring Security가 제공하는 인증 기능을 이용해 로그인 하거나 로그아웃 하는 기능도 사용할 수 있다. 

Servlet Spec 3.0 이상을 사용하는 WAS는 Tomcat 7.0 이상을 사용하면 된다. 


Spring Security가 자체적으로 제공하는 방법

<%@ 
page import="org.springframework.security.core.context.SecurityContextHolder" %>
<%@ page import="org.springframework.security.core.Authentication" %>
<%@ page import="com.terry.springsecurity.vo.MemberInfo" %>
<%
Authentication auth = SecurityContextHolder.getContext().getAuthentication();

Object principal = auth.getPrincipal();	
String name = "";
if(principal != null && principal instanceof MemberInfo){
	name = ((MemberInfo)principal).getName();
}
%>



위의 코드에서 보듯이 HttpServletRequest 클래스의 getUserPrincipal() 메소드를 이용해서 인증 정보를 가져오고 있다. 
로그인이 성공했을 경우엔 두 방법 모두 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 객체를 받아온다.
 그러나 로그인을 하지 않은, 즉 Anonymous 사용자인 경우엔 두 가지 방법에 차이점이 존재한다.Anonymous 사용자인 경우 Spring Security를 이용하는 방법에서는 org.springframework.security.authentication.AnonymousAuthenticationToken 객체가 return 되지만 HttpServletRequest를 이용하는 방법에서는 null이 return 되기 때문이다. 그래서 Spring Security를 이용하는 방법에서는 인증 정보를 받는 변수인 auth에 대한 null 체크를 하지 않지만 HttpServletRequest를 이용하는 방법에서는 인증 정보를 받는 변수인 auth에 대한 null 체크를 하게 되는 것이다.

<div style="width:200px;float:left;">

<sec:authorize access="isAnonymous()"> <form id="loginfrm" name="loginfrm" method="POST" 

action="${ctx}/j_spring_security_check"> <table> <tr> <td style="width:50px;">id</td> <td style="width:150px;"> <input style="width:145px;" type="text" id="loginid" name="loginid" value=""/> </td> </tr> <tr> <td>pwd</td> <td> <input style="width:145px;" type="text" id="loginpw" name="loginpw" value=""/> </td> </tr> <tr> <td colspan="2"> <input type="submit" id="loginbtn" value="로그인" /> </td> </tr> </table> </form> </sec:authorize> <sec:authorize access="isAuthenticated()"> <%=name%>님 반갑습니다<br/> <a href="${ctx}/j_spring_security_logout">로그아웃</a> </sec:authorize> <ul> <sec:authorize access="hasRole('ROLE_ADMIN')"> <li>관리자 화면</li> </sec:authorize> <sec:authorize access="permitAll"> <li>비회원 게시판</li> </sec:authorize> <sec:authorize access="isAuthenticated()"> <li>준회원 게시판</li> </sec:authorize> <sec:authorize access="hasAnyRole('ROLE_MEMBER2', 'ROLE_ADMIN')"> <li>정회원 게시판</li> </sec:authorize> </ul> </div>

 로그인 id test1에 준회원 권한을 DB에서 설정해 둔 뒤에 로그인을 한 모습이다. 
로그인 한 사람의 이름(테스트1)을 보여주고 있고 로그아웃 링크를 보여주고 있다. 또 준회원 권한(ROLE_MEMBER1)을 가지고 있기 때문에 비회원 게시판과 준회원 게시판 메뉴를 보여주고 있다. 관리자 권한(ROLE_ADMIN)을 주면 정회원 게시판과 관리자 화면 메뉴도 나타날 것이다. 이렇게 권한을 이용한 동적 메뉴 화면 모습을 보여주고 있다. 잘 이해가 안되면 <sec:authorize> 태그 설명한 부분을 다시 한번 읽어보면서 화면과 매핑을 시켜보면 이해가 될 것이라고 생각한다.


이제는 로그아웃을 설명할 차례이다. 지금까지 블로그에서 설명하면서 화면상에서 로그아웃을 보여주는 부분이 없어서 로그아웃에 대한 설정을 설명할 일이 없었으나 이제는 로그아웃이 언급되다보니 로그아웃 설정에 대한 설명을 할 시점이 되었다. 로그아웃을 실행시킬려면 j_spring_security_logout 으로 링크를 걸면 로그아웃 기능을 수행한다(j_spring_security_logout 이란 문자열이 길어서 거부감이 있다면 이것도 바꿀수 있다. 곧 이어 설명할 logout 태그의 logout-url 속성에 원하는 값을 지정해주면 된다. 이 속성의 default 값이 j_spring_security_logout 이다). 또한 로그아웃에 대한 설정을 <http> 태그 안에 다음과 같은 내용으로 넣도록 한다.

<logout 

	logout-success-url="/main.do"
	delete-cookies="JSESSIONID"
/>


logout-success-url 속성은 로그아웃 작업을 마친 뒤에 보여지는 화면 URL을 설정한다.

 delete-cookies는 로그아웃을 진행하면서 지워야 할 쿠키를 지정하는 속성이다. ,를 구분자로 하여 여러개의 쿠키 이름을 지정하면 된다. WAS를 통해 웹브라우저로 웹페이지를 보면 JSESSIONID란 이름의 쿠키가 내려오는데 로그아웃을 하면서 이 쿠키도 같이 정리하기 위해 delete-cookies 속성에 지정하는 것이다. 만약 다른 이름의 쿠키가 내려오거나 또는 사이트에서 로그인을 하면 내부적으로 특정 쿠키를 생성해서 작업하고 있다면 이 delete-cookies 속성을 이용해서 그런 부류의 쿠키를 지울수가 있는 것이다.

스프링 2번째_AOP개념

Web/Spring 2015. 7. 9. 18:04 Posted by Request

5)AOP 개념
(참조사이트 : http://najuung.tistory.com/65)

AOP 목적에서 횡단관심사를 모듈화하기 위하여 AOP를 만들었다고 하였다.
여기서 우리 AOP의 원론적인 의미를 알아야 할 필요가 있다.

Aspect 지향프로그래밍
횡단 관심사를 모듈화할 때 특별한 클래스로 모듈화 하는데 그 특별한 클래스가 바로 애스팩트라고 부른다.

AOP에서 가장 중요한 용어는 Advice(어드바이스), Point Cut(포인트 컷), 조인 포인트(Join Point) 3가지이다.

 

 

 

 

 

 


5.1) Advice
Aspect는 자신이 무엇을 해야 할 지 알고 있어야 한다.
(어드바이스 = 애스팩트가 해야 할 작업)

어드바이스는 액스팩트가 해야하는 작업에 대하여 언제 그 작업을 수행해야하는지 정의를 해준다.
어드바이스가 정의하는 언제는 메소드가 호출이전인지, 이후인지, 아니면 이전/이후 모두인지 그것도 아니면 예외를 던졌을 때인지를 의미한다.

 

*이전(Before) : 어드바이스 대상 메소드가 호출되기 전에 어드바이스 기능을 수행한다.
*이후(After)  : 결과에 상관없이 어드바이스 대상 메소드가 완료된 후에 어드바이스 기능을 수행한다.
*반환 이후 (after-returning) : 어드바이스 대상 메소드가 성공적으로 완료된 후에 어드바이스 기능을 수행한다.
*예외 발생 이후(after-throwing) : 어드바이스 대상메소드가 예외를 던진 후에 어드바이스 기능을 수행한다.
*주의 (around) : 어드바이스가 어드바이스 대상메소드를 감싸서 어드바이스 대상 메소드 호출 전과 후에 몇가지 기능을 제공한다.


5.2) Joinpoint
 애플리케이션 하나에도 어드바이스를 적용 할 있는 곳이 무수히 많으며 적용할 수 있는 곳을 조인 포인트라고 말한다.
즉, 조인포인트는 애플리케이션 실행에 애스팩트를 끼워 넣을 수 있는 지점이며 이러한 조인 포인트 지점은 메소드 호출지점이나
예외발생, 필드값 수정 등이 있다.


5.3)pointcut
한 애스팩트가 전체 애플리케이션의 모든 조인포인트를 다 어드바이스 할 수 없다.
이 때문에 포인트 컷은 애스팩트가 어드바이스할 조인포인트의 영역을 좁혀주는 역할을 한다.
어드바이스가 애스팩트가 무엇을 언제할지 정의한다면 포인트컷은 어디서를 정의하는 역할은 한다고 보면된다.


5.4)Aspect
애스팩트는 어드바이스와 포인트 컷을 합친 것이다.
두가지 정보가 합쳐지면 무엇을 언제할지에 대한 모든 정보가 정의된다.


5.5)인트로덕션
인트로덕션을 앞서 설명했던 AOP정점 중 기존 클래스에 코드변경없이 새 메소드가 멤버 변수를 추가하는 기능이다.
-예를 들어 어떤 객체의 상태가 언제 마지막으로 변경되었다라는 정보는 추가하는 경우를 생각해보자.


5.6)위빙
어드바이스를 핵심코드 로직에 적용하는 것을  위빙(weaving)이라고 한다.

어드바이스를 위빙하는 방식은 3가지 방식이 존재한다.

*컴파일 시에 위빙하기.
컴파일 시 코드를 삽입하는 방법은 aspectJ에서 사용하는 방식이다.
컴파일 방식에서는 핵심 로직을 구현한 자바 코드를 컴파일 할 때 알맞은 위치에 공통코드를 삽입한다.
공통코드 횡단관심사(애스팩트)를 모듈화한 코드를 말한다.

이렇게 컴파일된 코드는 AOP가 적용된 클래스 파일이 생성된다.
컴파일 방식을 제공하는 AOP 도구는 공통코드를 알맞은 위치에 삽입할 수 있도록 도와주는 컴파일러나 IDE를 제공한다.

*클래스 로딩시에 위빙하기.
AOP 라이브러리는 JVM이 클래스를 로딩할 때 클래스 정보를 변경할 수 있는 에이전트를 제공한다.
이 에이전트는 로딩한 클래스의 바이너리 정보를 변경하여 알맞은 위치에 공통 코드를 삽입한 새로운 클래스 바이너리 코드를 사용하도록 한다.


*런타임 시에 위빙하기
-스프링은 자체적으로 프록시 기반의 AOP를 지원하고 있음

런타임시 AOP를 적용할 때는 소스코드나 클래스 정보 자체를 변경하지 않는다. 대신 프록시를 이용하여 AOP를 적용한다.
프록시 기반의 AOP는 핵심로직을 구현한 객체에 직접 접근하는 것이 아니라 중간에 프록시를 생성하여 프록시를 통해 핵심로직을 구현한 객체에
접근하게 된다.
프록시는 핵심로직을 실행하기 전 또는 후에 공통기능을 적용하는 방식으로 aop를 적용한다.
프록시 기반에서는 메서드가 호출 될 때에만 어드바이스를 적용할 수 있기 때문에 필드 값 변경과 같은 조인 포인트에 대해서는 적용할 수 없는
한계가 있다.

 


6.spring에서의 AOP의 특징
6.1)스프링은 자체적으로 프록시 기반의 AOP를 지원한다.
그래서 스프링 AOP는 메서드 호출 조인포인트만을 지원한다.
만약 필드 값 변경 같은 조인포인트를 사용하고 싶다면 AspectJ같은 풍부한 기능을 지원하는 AOP도구를 사용해야 한다.
(JEE 어플리케이션을 구현하는데 필요한 수준의 기능만을 제공한다.)

6.2)스프링 AOP는 자바기반이다.
AspectJ는 애스팩트를 위한 별도의 문법을 제공하고 있는 반면 스프링은 별도의 문법을 익힐 필요 없이 자바언어를 이용하면 된다.

 

 


<aop:config> AOP 설정정보임을 나타냄

<aop:aspect> 한개의 aspect를 설정한다.
             aspect 태그의 ref속성에는 공통기능을 구현하고 있는 빈을 전달한다.

<aop:pointcut> point태그의 id속성은 pointcut를 구분하는데 사용되는 식별값을 입력받고 expression 속성은 pointcut를 정의하는
aspectJ의 표현식을 입력받는다.

<aop:around> around advice를 설정한다.

@Aspectc 어노테이션은 AspectJ 5번전에서 새롭게 추가된 어노테이션으로서 @Aspect 어노테이션을 사용하면 xml파일에 Advice 및 pointCut등의 설정을
하지 않고도 자동으로 advice를 할 수 있게 되었다.

 

스프링 구조파악_webxml

Web/Spring 2015. 7. 9. 17:44 Posted by Request

1.실행 구조 파악

1)WebXml.java


부가설명(1) :  스프링 3.1 web.xml 없는 스프링 개발
->스프링 4.0을 쓰고 있지만 기본적인 스프링 구조를 파악 할 겸 부가설명을 정리한다.

(참조사이트 : http://linuxism.tistory.com/1558)


서블릿 3.0을 사용하면 기존에 web.xml을 이용해서 서블릿 컨텍스트를 초기화하던 작업을 여러 파일로 쪼갤 수도 있고, 자바 코드를 이용할 수도 있다.
(자바코드와 web.xml 파일을 같이 사용할 수도 있고, 아예 web.xml 없이 자바코드만으로 초기화 작업을 할 수도 있다.
어차피 web.xml은 ServletContext 오브젝트 초기화하는 데 사용되던 메타정보니까 직접 ServletContext를 다루면 되는 것이겠지.)


부가설명(2)
위 1)의  Servlet-Context(.xml) 설정을 파악해봅니다.

일단 무엇을 설정 하는 파일인지 파일내 있는 영문 주석된 내용을 정리합니다.

 

(2.1)DispatcherServlet로 설정된 서블릿의 요청 처리 기반을 정의합니다.
(2.2)Spring MVC 패턴의 @Controller 프로그래밍 모델을 활성화 합니다. ( 어노테이션 사용이 가능)
(2.3)servlet의 url-pattern를 root( / )를 가져가는 url 패턴으로 사용한다면 확장자에 관계 없이 모든 요청에 대한 구분을 Dispatcherservlet이 통제 하려고 하기 때문에 css, js, img 파일등 처리도 모두 매핑해줘야 합니다.
그래서 번거러움을 없애기 위해서 DispatcherServlet이 특정폴더를 제외한 나머지만을 통제하게끔 설정하는 겁니다.
css, js, img 파일등 resources 폴더에 넣어 관리해준다.
<resources mapping="/resources/**" location="/resources/" />


(2.4)ViewResolver 설정을 한다. jsp와 name을 매핑시켜주는 역할을 합니다.
<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>


(2.5)com.exam.mod 하위 모든 패키지에 있는 파일의 어노테이션을 스캔해서 빈으로 등록하겠다는 것입니다.
<context:component-scan base-package="com.exam.mod" />

 

2) implements WebApplicationInitializer


3)onStartup(ServletContext serveltContext)

// 스프링 설정
        XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
        rootContext.setConfigLocations(new String[] { "classpath:config/spring/context-*.xml" });
        rootContext.refresh();
        rootContext.start();

        GlobalsProperties globalsProperties = rootContext.getBean("globalsProperties", GlobalsProperties.class);
        String serverType  = StringUtils.defaultString(globalsProperties.getProperty("server.type"), "local");
        String logoutUrl = StringUtils.defaultString(globalsProperties.getProperty("page.logout.url"), "");

 

// 스프링 MVC 설정
        XmlWebApplicationContext xmlWebApplicationContext = new XmlWebApplicationContext();
        xmlWebApplicationContext.setConfigLocation("classpath:config/spring/servlet-mvc.xml");
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(xmlWebApplicationContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");


4)servlet-mvc.xml

...
<context:component-scan base-package="com.el" use-default-filters="false">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
  <context:include-filter type="assignable" expression="com.el.eco.admin.common.WebControllerAdvice" />
<!--   <context:exclude-filter type="assignable" expression="com.el.core.framework.web.controller.ControllerAdvice" /> -->
 </context:component-scan>

 <mvc:resources mapping="/resources/**" location="/resources/" />

 <mvc:interceptors>
  <!-- 공통 인터셉터 -->
  <mvc:interceptor>
   <mvc:mapping path="/**" />
   <mvc:exclude-mapping path="/resources/**" />
   <bean class="com.el.core.framework.web.interceptor.ApplicationInterceptor" />
  </mvc:interceptor>
         </mvc:interceptors>
...

 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="order" value="3" />
        <property name="prefix" value="/WEB-INF/jsp/view/"/>
        <property name="suffix" value=".jsp"/>
        <property name="requestContextAttribute" value="rc"/>
        </bean>
 
        <aop:aspectj-autoproxy />

        
        <aop:config proxy-target-class="true">
        //인터페이스를 구현하지 않은 타깃 클래스에 AOP를 적용하기위해쓰는 설정입니다.


        <aop:aspect id="aspectLoggging" ref="loggingAspect">
  <aop:pointcut id="controllerPointcut" expression="execution(* com.el..*Act.*(..))" />
         <aop:around method="controllerAround" pointcut-ref="controllerPointcut"  />
 </aop:aspect>