현재 전체 론적 시스템을 마이크로 서비스 아키텍처로 마이그레이션하는 솔루션을 찾고 있습니다. Spring Integration과 Spring Security를 사용하여 서비스를 통합하고 보안합니다. 내 이해에 따르면 백엔드 서비스를 보호하는 것은 SSO (Single Sign On)와 같습니다. Jasig CAS 4.2.7 (Spring Security와 잘 작동하는 것 같습니다)을 사용하여 사용자를 중앙에서 인증하고, Spring Integration 4.2.11.RELEASE 및 Spring Security 4.0.4.RELEASE를 사용합니다.CAS와 Spring 통합을 사용할 때 보안 백엔드 서비스에 액세스하는 방법은 무엇입니까?
웹 응용 프로그램 인 web 및 service라는 두 개의 모듈로 Maven 프로젝트를 만들었습니다. 3 개의 war 파일을 동일한 로컬 Tomcat (버전 7.0.36)에 배포하고 jimi와 bob을 CAS 등록 정보 파일에 추가하여 CAS의 인증을 통과하도록합니다. URL http://localhost:8080/prototype-integration-security-web/user에 액세스하려고하면 프런트 엔드 응용 프로그램에 인증되지만 백엔드 서비스에서는 액세스가 금지됩니다.
POM 파일은 다음과 같습니다.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>prototype.integration.security</groupId>
<artifactId>prototype-integration-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>prototype-integration-security</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warName>${project.name}</warName>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-http</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-security</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
<modules>
<module>prototype-integration-security-web</module>
<module>prototype-integration-security-service</module>
</modules>
</project>
두 모듈의 배치 설명 파일 web.xml은 다음과 같은 표시 이름을 제외하고는 동일하게 보입니다.
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="IntegrationSecurityWeb" version="3.0">
<display-name>Integration Security Web Prototype</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<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>
</web-app>
웹 모듈의 Spring 응용 프로그램 컨텍스트 구성 파일에서 dispatcher-servlet.xml은 다음과 같습니다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-4.2.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd">
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
<property name="targetMethod" value="createMinimal"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
</list>
</property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/prototype-integration-security-web/login/cas" />
<property name="sendRenew" value="false" />
</bean>
<!-- Access voters -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg>
<bean class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login" />
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="http://localhost:8080/cas/logout" />
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas" />
</bean>
<security:http entry-point-ref="casEntryPoint" access-decision-manager-ref="accessDecisionManager" use-expressions="false">
<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login />
<security:logout />
<security:custom-filter before="LOGOUT_FILTER" ref="requestSingleLogoutFilter"/>
<security:custom-filter before="CAS_FILTER" ref="singleLogoutFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<security:user-service id="userService">
<security:user name="jimi" password="jimi" authorities="ROLE_ADMIN" />
<security:user name="bob" password="bob" authorities="ROLE_USER" />
</security:user-service>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg index="0" ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="localCAS" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<int:channel-interceptor order="99">
<bean class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"/>
</int:channel-interceptor>
<task:executor id="pool" pool-size="5"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
<int-security:secured-channels>
<int-security:access-policy pattern="user*" send-access="ROLE_USER" />
<int-security:access-policy pattern="admin*" send-access="ROLE_ADMIN" />
</int-security:secured-channels>
<int-http:inbound-channel-adapter path="/user*" supported-methods="GET, POST" channel="userRequestChannel" />
<int:channel id="userRequestChannel">
<int:queue/>
</int:channel>
<int-http:outbound-channel-adapter url="http://localhost:8080/prototype-integration-security-service/query?ticket={ticket}"
http-method="GET"
rest-template="restTemplate"
channel="userRequestChannel">
<int-http:uri-variable name="ticket" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials"/>
</int-http:outbound-channel-adapter>
<int-http:inbound-channel-adapter path="/admin/callback*"
supported-methods="GET, POST"
channel="adminRequestChannel" />
<int:channel id="adminRequestChannel">
<int:queue/>
</int:channel>
<int:logging-channel-adapter id="logging" channel="adminRequestChannel" level="DEBUG" />
</beans>
서비스 모듈의 컨텍스트 구성 파일에서 dispatcher-servlet.xml은 다음과 같습니다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-4.2.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd">
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
<property name="targetMethod" value="createMinimal"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
</list>
</property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/prototype-integration-security-service/login/cas" />
<property name="sendRenew" value="false" />
</bean>
<!-- Access voters -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg>
<bean class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
<!-- <bean class="org.springframework.security.web.access.expression.WebExpressionVoter" /> -->
</list>
</constructor-arg>
</bean>
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login" />
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://localhost:8443/cas/logout" />
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas" />
</bean>
<security:http entry-point-ref="casEntryPoint" access-decision-manager-ref="accessDecisionManager" use-expressions="false">
<security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
<security:form-login />
<security:logout />
<security:custom-filter before="LOGOUT_FILTER" ref="requestSingleLogoutFilter"/>
<security:custom-filter before="CAS_FILTER" ref="singleLogoutFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<security:user-service id="userService">
<security:user name="jimi" password="jimi" authorities="ROLE_ADMIN" />
<security:user name="bob" password="bob" authorities="ROLE_USER" />
</security:user-service>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg index="0" ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="localCAS" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<int:channel-interceptor order="99">
<bean class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"/>
</int:channel-interceptor>
<task:executor id="pool" pool-size="5"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
<int-security:secured-channels>
<int-security:access-policy pattern=".*" send-access="ROLE_ADMIN" />
</int-security:secured-channels>
<int-http:inbound-channel-adapter path="/query*" supported-methods="GET, POST" channel="requestChannel" />
<int:channel id="requestChannel">
<int:queue/>
</int:channel>
<int-http:outbound-channel-adapter url="http://localhost:8080/prototype-integration-security-web/admin/callback?ticket={ticket}"
http-method="GET"
rest-template="restTemplate"
channel="requestChannel">
<int-http:uri-variable name="ticket" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials" />
</int-http:outbound-channel-adapter>
</beans>
추가 코드가 필요하지 않으므로 이것이 스프링 통합을 좋아합니다. 잘못되었거나 구성이 누락 되었습니까? 아이디어, 의견 및 제안을 공유하십시오. 미리 감사드립니다.
감사합니다. 나는 다음과 같이 시도했지만 행운이 없다면 티켓이 MultipleValueMap의 public이 아니라는 것을 알 수있다. 티켓 키 - 값 쌍이 없다는 것을 의미한다. ' CasAuthenticationFilter가 티켓을 remvoed했을 수도 있고 어딘가에 티켓을 보관할 자체 필터를 구현해야 할 수도 있습니다. –
제 답변에서 업데이트를 찾으십시오. –
안녕하세요, Artem, 많은 의견에 감사 드리며 지금은 ServiceContextHolder에서 Service Ticket을받을 수 있습니다. 그러나 아직 작동하지 않습니다, 내 액세스가 403 오류로 거부되었습니다. 백엔드 서비스는 웹 모듈의 원래 outbound-channel-adapter를 대체하는 outbound-gateway의'transfer-cookie = "true"'를 설정하여 쿠키를 전송하려고 시도하지만 티켓을 전혀 검증하지 않는 것 같습니다. –