목차 >> Glue Security API 기능 +- DB 설계 +- 로그인기능 ----+- GlueUserDetailsService ----+- security-service ----+- security-query.glue_sql +- Resource 권한정의 ----+- <custom-filter> ----+- GlueInvocationDefinitionSource ----+- GlueServiceFilter +- View Component 권한관리 ----+- GlueSecurityUtil 사용 ----+- Tag 활용 +- Resource Reload |
GlueSecurity 에서는 Spring Security기반에서 Security기능을 제공하고 있으면 인증과 권한 관련 정보를 기본적DB에서 가져오도록 하고 있다.
Table 명 | 설명 |
---|---|
USERS | User 기본 정보(id,password) |
GROUPS | Group 정보(User는 하나의 Group에 소속) |
GROUPS_ROLES | Group과 Role 관계 정보(각 Group이 가지고 있는 Role 정보) |
ROLES | Role 리스트 |
RESOURCES_ROLES | Resource와 Role 관계 정보(각 Role이 접근 가능한 Resource 정보) |
RESOURCES | Resource 리스트 |
VIEW_AUTHORITY | View Component 리스트 |
VIEW_ROLES | View Component와 Role 관계 정보(각 Role이 접근 가능한 View Component 정보) |
Glue Framework 기반의 Web Application에서 로그인 기능을 사용하기 위해서는 <authentication-manager>의 user-service로 GlueUserDetailsService를 사용한다.
GlueUserDetailsService가 정상적으로 실행되기 위해서는 security-service.xml와 security-query.glue_sql이 해당 프로젝트에 등록되어야 한다.
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="glueUserService" > <password-encoder hash="sha"> <salt-source user-property="username"/> </password-encoder> </authentication-provider> </authentication-manager> <beans:bean id="glueUserService" class="com.poscoict.glueframework.security.bean.GlueUserDetailsService"/>
Glue Security Manger의 사용자 인증정보(USERS)의 패스워드는 sha encoding이 적용되서 관리된다. 로그인시 입력받은 ID와 패스워드를 비교하기 위해 패스워드의 인코딩이 필요하므로 password-encoder가 필요하다.
GlueUserDetailsService는 6개의 property를 갖으며, 생략시 default 값은 다음과 같다.
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="glueUserService" > <password-encoder hash="sha"> <salt-source user-property="username"/> </password-encoder> </authentication-provider> </authentication-manager> <beans:bean id="glueUserService" class="com.poscoict.glueframework.security.bean.GlueUserDetailsService"> <beans:property name="serviceName" value="security-service"/> <beans:property name="viewAuthorityManagerId" value="viewAuthorityManager"/> <beans:property name="serviceAuthorityManagerId" value="serviceAuthorityManager"/> <beans:property name="crc" value="false"/> <beans:property name="userName" value="user_id"/> <beans:property name="crcName" value="crc_id"/> </beans:bean>
GlueUserDetailsService는 org.springframework.security.core.userdetails.UserDetailsService를 상속받아 구현 되었으며 security-service가 기본으로 실행된다. 실행되는 Service 를 다른 서비스로 바꾸고 싶을 경우에는 property값으로 serviceName을 주면 된다.
Glue 서비스 실행 후 GlueContext로부터 ‘users’ 와 ‘roles’ 를 Key로 해당 객체를 가져오며 UserDetailsService의 구현 메서드인 loadUserByUsername에서는 GlueSecurityUserImpl 객체가 반환된다.
User 검색에서 User_ID를 사용하는데 User_ID는 varchar형의 컬럼이다. 일반적으로 varchar값 인덱스의 속도보다는 int값 인덱스와 속도가 훨씬 빠르다. User가 매우 많을 경우 Crc32로 암호화한 int값을 사용하여 검색하면 검색 속도를 빠르게 할 수 있다.
GlueUserDetailsService에서는 property로 crc를 true로 설정하면 User 검색에서 Crc_id를 사용한다.
GlueUserDetailsService가 정상적으로 실행되기 위해서는 Glue 서비스로 security-service와 쿼리 파일로 security-query.glue_sql이 해당 프로젝트에 존재해야 한다.
security-service라는 서비스 명은 property 값으로 변경 가능하며 security-query.glue_sql등록된 쿼리는 security-service 내용을 변경하여 수정 가능하다.
그리고 필수 항목은 아니지만 각 화면 구성요소의 권한을 User에 설정하여 관리하기 위해서는 ApplicationContext에 glueViewAuthorityManager가 등록되어있어야 한다.
<bean id="viewAuthorityManager" class="com.poscoict.glueframework.security.authority.GlueViewAuthorityDbManagerImpl"> <property name="jdbcDao" ref="securityDao "/> </bean>
GlueUserDetailsService가 정상적으로 실행되기 위해서는 Glue 서비스로 security-service와 쿼리 파일로 security-query.glue_sql이 해당 프로젝트에 존재해야 한다. security-service와 security-query.glue_sql는 sample로 제공되며 GlueUserDetailsService에서 실행되는 서비스를 security-service가 아닌 다른 서비스로 설정하고 싶을 경우에는 GlueUserDetailsService에서 property값으로 serviceName을 주면 된다.
<beans:bean id="glueUserService" class="com.poscoict.glueframework.security.bean.GlueUserDetailsService"> <beans:property name="serviceName" value="security-service"/> </beans:bean>
Sample 로 제공되는 security-service에서는 GlueJdbcSearch를 이용하여 User,Role 정보를 가져오도록 설정되어 있다.
프로젝트에 따라서 로그인 로직이 변경되어야 하는 경우 GlueJdbcSearch가 아닌 Custom Class로 설정할 수 있다. Custom Class를 작성하는 경우 GlueContex에 User정보와 Role 정보가 담겨야 한다. User정보는 ‘users’를 key로 하여 ListGlueUser 형태로 저장되어야 하며 Role 정보는 ‘roles’ 를 key로 하여 ListGlueRole 형태로 저장되어야 한다.
Spring Security에서는 기본적으로 URL에 대한 권한 정보를 아래와 같이 xml에서 정의하고 있다.
<http auto-config="true"> <intercept-url pattern="/admin/*" access="ROLE_ADMIN" /> <intercept-url pattern="/user/*" access="ROLE_USER" /> </http>
Glue Security에서는 URL에 대한 권한 정보를 xml에 정의하지 않고 Manger화면을 통해 DB에 관리한다. 그래서 <intercept-url> 대신에 <custom-filter>를 사용한다.
<http auto-config="true"> <custom-filter ref="filterInvocationInterceptor" after="FILTER_SECURITY_INTERCEPTOR" /> <form-login login-page="/login.jsp" /> </http> <beans:bean id="filterInvocationInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor"> <beans:property name="observeOncePerRequest" value="false" /> <beans:property name="securityMetadataSource" ref="glueInvocationDefinitionSource" /> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="accessDecisionManager" ref="accessDecisionManager" /> </beans:bean> <beans:bean id="glueInvocationDefinitionSource" class="com.poscoict.glueframework.security.bean.GlueInvocationDefinitionSource"/>
<intercept-url>로 정의된 정보와 DB에 정의된 정보가 중복되면 접근 권한 관련된 기능이 오동작 할 수 있으므로 Resource 권한 정보의 정의는 DB와 xml 둘 중 하나만 사용하도록 한다.
Glue Security Manger의 Resource 화면을 통해 2가지 유형의 리소스를 관리 할 수 있다. 그것은 URL과 SERVICE 이다. ( Resource Page 참고)
Glue Security에서는 2가지 유형의 <custom-filter>를 사용할 수 있다.
<http auto-config="true"> <custom-filter ref="filterInvocationInterceptor" after="FILTER_SECURITY_INTERCEPTOR" /> <custom-filter ref="gluefilter" before="FILTER_SECURITY_INTERCEPTOR" /> <form-login login-page="/login.jsp"/> </http>
<custom-filter> 설정에서 [after="FILTER_SECURITY_INTERCEPTOR"] 인 경우에는 filterInvocationInterceptor가 FILTER_SECURITY_INTERCEPTOR 동작 이후에 작동하고 [before="FILTER_SECURITY_INTERCEPTOR"]인 경우에는 filterInvocationInterceptor가 FILTER_SECURITY_INTERCEPTOR 동작 이전에 작동한다.
Glue Security Manger의 Role 화면을 통해 리소스를 관리 하며, URL 리소스는 filterInvocationInterceptor 를 통해 SERVICE 리소스는 gluefilter 를 통해 처리한다. ( Role Page 참고)
<beans:bean id="filterInvocationInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor"> <beans:property name="observeOncePerRequest" value="false" /> <beans:property name="securityMetadataSource" ref="glueInvocationDefinitionSource" /> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="accessDecisionManager" ref="accessDecisionManager" /> </beans:bean> <beans:bean id="glueInvocationDefinitionSource" class="com.poscoict.glueframework.security.bean.GlueInvocationDefinitionSource"/> <beans:bean id="gluefilter" class="com.poscoict.glueframework.security.filter.GlueServiceFilter"/>
URL type에 대한 Resource 권한 정보를 DB에서 가져오기 위해서는 <custom-filter>로 org.springframework.security.web.access.intercept.FilterSecurityInterceptor 이 사용된다. FilterSecurityInterceptor 는 observeOncePerRequest 속성, securityMetadataSource속성, authenticationManager 속성, accessDecisionManager 속성을 갖는다. ROLE_ID는 기본적으로 “ROLE_”라는 prefix 가 사용된다. Role prefix를 변경하고자 한다면 아래와 같이 accessDecisionManager 의 RoleVoter를 수정하면 된다.
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> <beans:property name="allowIfAllAbstainDecisions" value="true" /> <beans:property name="decisionVoters"> <beans:list> <beans:bean class="org.springframework.security.access.vote.RoleVoter" > <beans:property name="rolePrefix" value="ROLE_"/> </beans:bean> </beans:list> </beans:property> </beans:bean>
SERVICE type에 대한 Resource 권한 정보는 <custom-filter>로 com.poscoict.glueframework.security.filter.GlueServiceFilter를 설정해 준어야 한다.
GlueInvocationDefinitionSource는 DB로부터 URL Resource 정보를 가져오도록 되어 있으며 GlueInvocationDefinitionSource가 정상 동작 하기 위해서는 security-service가 있어야 하며 쿼리 ID로 security.urlRepository.select(예제 소스의security-query.glue_sql 참고)가 등록 되어 있어야 한다.
다음은 API 모듈(glue-security-access-{version}.jar) 에 포함된 security-service.xml의 일부이다.
<service . . .> <activity name="getUrlPattern" class="com.poscoict.glueframework.biz.activity.GlueJdbcSearch"> <transition name="success" value="end"/> <property name="sql-key" value="security.urlRepository.select"/> <property name="dao" value="securityDao"/> <property name="param-count" value="0"/> <property name="result-key" value="ResourcePattern"/> </activity> . . .중략 . . .
GlueInvocationDefinitionSource는 security-service을 실행 시키고, 결과값은 ResourcePattern을 통해 획득한다. GlueInvocationDefinitionSource은 2개의 property를 갖으며, default 값은 다음과 같다.
<beans:bean id="glueInvocationDefinitionSource" class="com.poscoict.glueframework.security.GlueInvocationDefinitionSource"> <beans:property name="glueService" value="security-service" /> <beans:property name="resultKey" value="ResourcePattern"/> </beans:bean>
프로젝트에 따라서 Resource 정보를 가져오는 로직이 변경되어야 하는 경우 GlueJdbcSearch가 아닌 Custom Class로 설정할 수 있다. Custom Class를 작성하는 경우 GlueContex에 Resource 권한 정보가 담겨야 한다. Resource 권한 정보는 ‘ResourcePattern’를 key로 하여 List<Resource> 형태로 저장되어야 한다. ‘ResourcePattern’ 말고 다른 key를 사용하고자 한다면, Property 값으로 resultKey를 정의하면 된다.
Glue Security에서는 해당 User에게 특정 Service를 실행할 수 있는 권한이 있는가를 체크하는 기능을 제공하고 있다. <custom-filter>로 등록한 GlueServiceFilter를 통해 GlueService의 실행권한을 체크할 수 있다.
<http auto-config="true"> <custom-filter ref="filterInvocationInterceptor" after="FILTER_SECURITY_INTERCEPTOR" /> <custom-filter ref="gluefilter" before="FILTER_SECURITY_INTERCEPTOR" /> <form-login login-page="/login.jsp"/> </http> <beans:bean id="gluefilter" class="com.poscoict.glueframework.security.filter.GlueServiceFilter"/>
GlueServiceFilter는 1개의 property를 갖으며, default 값은 다음과 같다.
<beans:bean id="gluefilter" class="com.poscoict.glueframework.security.filter.GlueServiceFilter"> <beans:property name="serviceAuthorityManagerId" value="serviceAuthorityManager"/> </beans:bean>
Glue Service의 실행 권한을 체크하기 위해서는 Service의 실행 권한 정보를 읽어오는 serviceAuthorityanager 가 필요하다. serviceAuthorityanager는 applicationContext.xml에 등록된 bean id 이며, com.poscoict.glueframework.security.authority.GlueServiceAuthorityManager 구현체이다. Glue Security API에서는 DB로부터 Service의 실행 권한을 읽어오는 GlueServiceAuthorityDbManagerImpl을 제공하고 있다.
GlueServiceAuthorityDbManagerImpl은 2개의 property 갖고, default 값은 아래와 같다.
<bean id="serviceAuhorityManager" class="com.poscoict.glueframework.security.authority.GlueServiceAuthorityDbManagerImpl"> <property name="jdbcDao" ref="securityDao"/> <property name="queryKey" value="security.serviceRoles.select"/> </bean>
jdbcDao는 Security DB에 접근 가능한 GlueJdbcDao를 설정하면 된다.
Glue Security Manager를 통해, 화면에 표시할 View Component를 관리할 수 있다. ( View Page 참고)
로그인시 생성된 User 객체에는 View Component 권한 정보를 포함시킬 수 있다. 그러기 위해서는 applicationContext.xml에는 com.poscoict.glueframework.security.authority.GlueViewAuthorityManager 구현체가 등록되어 있어야 한다.
GlueViewAuthorityManager는 다음과 같다.
public interface GlueViewAuthorityManager { public List<GlueViewAuthority> getViewAuthorities(List<GlueRole> roles); }
로그인 인증이 완료되면, User 객체가 생성되고 List<GlueViewAuthority> 구조체로 View Component가 설정된다.
GlueUserDetailsService의 viewAuthorityManagerId에 등록한 bean id를 설정하고, applicationContext.xml 의 구현체를 등록한다.
<beans:bean id="glueUserService" class="com.poscoict.glueframework.security.bean.GlueUserDetailsService"> <beans:property name="serviceName" value="security-service"/> <beans:property name="viewAuthorityManagerId" value="viewAuthorityManager"/> <beans:property name="serviceAuthorityManagerId" value="serviceAuthorityManager"/> <beans:property name="crc" value="false"/> <beans:property name="userName" value="user_id"/> <beans:property name="crcName" value="crc_id"/> </beans:bean>
Glue Security API에서는 DB로부터 View Component 권한정보를 읽어오는 GlueViewAuthorityDbManagerImpl 을 제공하고 있다. GlueViewAuthorityDbManagerImpl 은 2개의 property 갖고, default 값은 아래와 같다.
<bean id="viewAuthorityManager" class="com.poscoict.glueframework.security.authority.GlueViewAuthorityDbManagerImpl"> <property name="jdbcDao" ref="securityDao"/> <property name="queryKey" value="security.viewRoles.select"/> </bean>
GlueSecurityUtil에서는 로그인시 생성된 User객체를 통해 화면의 Component에 대한 권한이 있는지 없는지를 체크하는 hasAuthority메서드를 제공하고 있다.
메서드 | 설명 |
---|---|
boolean hasAuthority(String roleType) | pageId는 ALL, componentType는 ALL , roleType은 파라미터 roleType 값인 권한이 있으면 true 없으면 false |
boolean hasAuthority(String pageId,String roleType) | pageId는 ALL, componentType는 파라미터 componentType, roleType은 파라미터 roleType 값인 권한이 있으면 true 없으면 false |
boolean hasAuthority(String pageId,String componentType,String roleType) | pageId는 파라미터 pageId, componentType는 파라미터 componentType, roleType은 파라미터 roleType 값인 권한이 있으면 true 없으면 false |
JSP 화면에서는 아래와 같은 방식으로 사용할 수 있다.
<title>Sample</title> . . . . <% if(GlueSecurityUtil.hasAuthority("use")){%> <input type=submit name="save"> <% }else{ %> <input type=submit name="save" disabled="true"> <% } %> <BR/>
GlueUserDetailsService에서 설정된 User정보를 기반으로 각 화면의 Component에 접근 권한이 있는지 없는지를 체크하는 Tag를 만들어 사용할 수 있다. (JSP 2.0 이상)
Tag 를 설정하기 위해서는 hasAuth.tag파일을 만든고, WEB-INF밑의 tags폴더에 넣어두어야 한다.
[WEB-INF] ├ [lib] ├ [security] ├ [tags] │ └ hasAuth.tag ├ dispatcher-servlet.xml └ web.xml
hasAuth.tag는 다음과 같이 만든다.
<%@ tag body-content="scriptless" pageEncoding="utf-8" description="" %> <%@ attribute name="page" %> <%@ attribute name="component" %> <%@ attribute name="role" required="true" %> <%@ tag import="com.poscoict.glueframework.security.util.GlueSecurityUtil" %> <% if(page == null ) page = "ALL"; if(component == null) component = "ALL"; if(GlueSecurityUtil.hasAuthority(page,component,role)){ %> <jsp:doBody/> <% } %>
태그를 사용하고자 하는 JSP에서는 tagdir로 ‘/WEB-INF/tags’를 선언해 주면 hasAuth태그를 사용할 수 있다. hasAuth 태그 사용 예는 아래와 같다.
. . . 중략 . . . <%@ taglib tagdir="/WEB-INF/tags" prefix="glue"%> <html> . . . 중략 . . . <body> . . . 중략 . . . <glue:hasAuth role="use"><input type=submit name="save"></glue:hasAuth> . . . 중략 . . . </body> </html>
태그 attribute값으로 page, component, role값을 설정 해 줄 수 있으며 role은 필수 값이며 page, component는 설정하지 않으면 ALL로 설정한다.
page, component, role값에 해당하는 권한이 있으면 태그 내의 값이 표시되고 그렇지 않으면 표시되지 않는다.
위와 같이 태그를 사용하였을 경우 pageId는 ALL, componentType은 ALL, roleType은 WRITE인지. GlueSecurityUitl의 hasAuthority()를 이용한다. view component 권한이 있으면 화면에 태그 내의 내용이 표시되고, 즉 save 버튼이 표시된다. view component 권한이 없으면 아무것도 표시되지 않는다.
권한 관련 정보들은 서버가 시작되면서 Bean에 로딩되기 때문에 서버가 시작된 후에 관련 테이블의 정보가 수정되어도 반영되지 않는다.
Glue Security API에서는 서버가 시작된 후에 수정된 정보를 반영하기 위해서 권한 정보를 Reload해주는 Controller를 제공하고 있다
설정 방법
web.xml에 security Dispatcher 서블릿을 등록하고 ,
<servlet> <servlet-name>security</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>security</servlet-name> <url-pattern>/SecurityReload</url-pattern> </servlet-mapping>
{security}-servlet.xml에 권한 정보를 Reload해주는 Controller를 아래와 같이 추가해 주면 된다.
<?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:context="http://www.springframework.org/schema/context" xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd"> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <value> /SecurityReload=controller </value> </property> </bean> <bean name="controller" class="com.poscoict.glueframework.security.controller.GlueSecurityResourceReload" /> <bean id="viewResolver" name="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/> </beans>
이때 GlueSecurityResourceReload는 ModelAndView가 jsonView라는 name으로 생성되기 때문에 viewResolver로 jsonView가 존재해야 한다.
Reload 방법
{dispatcher-servlet-name}-servlet.xml에 등록된 GlueSecurityResourceReload를 호출하면 권한 정보를 해당 Bean에 Reload해 준다.
이 때 파라미터로 Reload하고 싶은 정보의 종류를 ReloadType으로 넘겨주어야 한다. (예 : http://127.0.0.1:8080/glue-security1/SecurityReload? ReloadType=URL)
ReloadType값으로는 URL, SERVICE, VIEW을 설정할 수 있다.
Glue Security Manager의 Server Page를 통해 권한정보를 갱신하고자 하는 Server정보를 관리할 수 있다. Server Page에 URL, SERVICE, VIEW 갱신 버튼을 제공하고 있다. ( Server Page 참고)
URL은 각 권한으로 접근가능한 URL 정보를 Reload(GlueInvocationDefinitionSource 참고)하고, SERVICE는 각 권한으로 실행 가능한 Glue SERVICE 정보를 Reload(GlueServiceAuthorityManager 참고)하고, VIEW는 각 권한으로 접근 가능한 VIEW COMPONENT 정보를 Reload (GlueViewAuthorityManager 참고)한다.