Одной из новых функций в AppFuse 2.1 является архетип appfuse-ws . Этот архетип использует Enunciate и CXF для создания проекта с REST API и сгенерированной HTML-документацией. Enunciate — это очень полезный инструмент, позволяющий разрабатывать веб-сервисы с аннотациями JAX-RS и JAX-WS и создавать все типы клиентских библиотек. Для меня это кажется очень полезным для разработки серверной части приложений SOFEA (современных).
Еще в марте Райан Хитон опубликовал прекрасную статью о защите веб-сервисов в приложении Enunciate. Я решил продвинуть его руководство на шаг вперед и не только защитить свои веб-службы, но и интегрировать его с OAuth 2. В этом руководстве я покажу вам, как создать новое приложение с AppFuse WS, защитить его, добавить поддержку OAuth , а затем использовать клиентское приложение для проверки подлинности и получения данных.
- Создайте новый проект AppFuse WS
- Интеграция Spring Security и OAuth
- Аутентификация и получение данных с клиентом
Создание нового проекта AppFuse WS
Для начала я посетил страницу « Создание архетипов AppFuse» и создал новое приложение, используя опцию «Только веб-сервисы» в раскрывающемся меню Web Framework . Ниже приведена команда, которую я использовал для создания проекта appfuse-oauth.
mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes \ -DarchetypeArtifactId=appfuse-ws-archetype -DarchetypeVersion=2.1.0 \ -DgroupId=org.appfuse.example -DartifactId=appfuse-oauth
После этого я запустил приложение, используя mvn jetty: run, и подтвердил, что все в порядке. На этом этапе я смог просмотреть сгенерированную документацию для приложения по адресу http: // localhost: 8080 . На скриншоте ниже показано, как выглядит приложение на данный момент.
ПРИМЕЧАНИЕ. Вы можете заметить конечную точку REST / {username}. Эта ошибка в AppFuse 2.1.0 была исправлена в SVN. Это не влияет на этот урок.
Интеграция Spring Security и OAuth
Первоначально я пытался интегрировать Spring Security с руководством Enunciate по защите веб-сервисов . Тем не менее, он только защищает конечные точки и не выполняет достаточную фильтрацию для поддержки OAuth, поэтому я использовал собственный web.xml. Я поместил этот файл в src / main / resources и загрузил его в свой файл enunciate.xml . Я также обновил Spring Security и импортировал свой файл security.xml.
<?xml version="1.0"?>
<enunciate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.22.xsd">
...
<webapp mergeWebXML="src/main/resources/web.xml"/>
<modules>
...
<spring-app disabled="false" springVersion="3.0.5.RELEASE">
<springImport uri="classpath:/applicationContext-resources.xml"/>
<springImport uri="classpath:/applicationContext-dao.xml"/>
<springImport uri="classpath:/applicationContext-service.xml"/>
<springImport uri="classpath:/applicationContext.xml"/>
<springImport uri="classpath:/security.xml"/>
</spring-app>
</modules>
</enunciate>
Затем я создал src / main / resources / web.xml с фильтром для Spring Security и DispatcherServlet для поддержки OAuth.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<filter>
<filter-name>securityFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSecurityFilterChain</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>appfuse-oauth</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appfuse-oauth</servlet-name>
<url-pattern>/oauth/*</url-pattern>
</servlet-mapping>
</web-app>
Затем я создал файл src / main / resources / security.xml и использовал его для защиты своего API, указания страницы входа, предоставления пользователей и интеграции OAuth (см. Последние 4 компонента ниже).
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd">
<http auto-config="true">
<intercept-url pattern="/api/**" access="ROLE_USER"/>
<intercept-url pattern="/oauth/**" access="ROLE_USER"/>
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true"
login-processing-url="/j_security_check"/>
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_USER,ROLE_ADMIN"/>
<user name="user" password="user" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
<!--hook up the spring security filter chain-->
<beans:alias name="springSecurityFilterChain" alias="securityFilter"/>
<beans:bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.InMemoryOAuth2ProviderTokenServices">
<beans:property name="supportRefreshToken" value="true"/>
</beans:bean>
<oauth:provider client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:verification-code user-approval-page="/oauth/confirm_access"/>
</oauth:provider>
<oauth:client-details-service id="clientDetails">
<!--<oauth:client clientId="my-trusted-client" authorizedGrantTypes="password,authorization_code,refresh_token"/>
<oauth:client clientId="my-trusted-client-with-secret"
authorizedGrantTypes="password,authorization_code,refresh_token" secret="somesecret"/>
<oauth:client clientId="my-less-trusted-client" authorizedGrantTypes="authorization_code"/>-->
<oauth:client clientId="ajax-login" authorizedGrantTypes="authorization_code"/>
</oauth:client-details-service>
</beans:beans>
Я использовал примеры приложений OAuth для Spring Security, чтобы понять это. В этом примере я использовал authorGrantTypes = «authorization_code», но из приведенных выше элементов <oauth: client> видно, что есть несколько разных вариантов. Вы также должны заметить, что clientId жестко запрограммирован как «ajax-login», что означает, что я хочу разрешить аутентификацию только одному приложению.
На данный момент, я хотел бы дать Shoutout Райан Хитон для создания как излагают и поддержка OAuth Spring Security. Отличная работа, Райан!
На этом этапе мне нужно было выполнить ряд дополнительных задач, чтобы завершить интеграцию oauth. Первым было изменить конфигурацию подключаемого модуля Jetty: 1) запустить на порту 9000, 2) загрузить мои пользовательские файлы и 3) разрешить jetty: запустить для распознавания сгенерированных файлов Enunciate. Ниже приведена окончательная конфигурация в моем pom.xml.
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
<configuration>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>9000</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<webAppConfig>
<baseResource implementation="org.mortbay.resource.ResourceCollection">
<resourcesAsCSV>
${basedir}/src/main/webapp,
${project.build.directory}/${project.build.finalName}
</resourcesAsCSV>
</baseResource>
<contextPath>/appfuse-oauth</contextPath>
</webAppConfig>
<webXml>${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml</webXml>
</configuration>
</plugin>
Затем я добавил необходимые OAuth-зависимости для Spring Security в свой pom.xml. Поскольку последний выпуск является вехой, мне также пришлось добавить репозиторий Spring.
<repository>
<id>spring-milestone</id>
<url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
</repository>
...
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-support</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth</artifactId>
<version>1.0.0.M3</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
Поскольку я назвал свой DispatcherServlet «appfuse-oauth» в web.xml, я создал src / main / webapp / WEB-INF / appfuse-oauth-servlet.xml для настройки Spring MVC. Мне пришлось создать каталог src / main / webapp / WEB-INF .
<?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:mvc="http://www.springframework.org/schema/mvc"
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">
<!-- Scans the classpath of this application for @Components to deploy as beans -->
<context:component-scan base-package="org.appfuse.examples.webapp"/>
<!-- Configures the @Controller programming model -->
<mvc:annotation-driven/>
<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
Чтобы показать страницу подтверждения OAuth, мне нужно было создать файл src / main / java / org / appfuse / examples / webapp / AccessConfirmationController.java и сопоставить его с / oauth / verify_access. Я скопировал это из одного из примеров проектов и изменил, чтобы использовать аннотации Spring.
package org.appfuse.examples.webapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.ClientAuthenticationToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.verification.ClientAuthenticationCache;
import org.springframework.security.oauth2.provider.verification.DefaultClientAuthenticationCache;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.TreeMap;
/**
* Controller for retrieving the model for and displaying the confirmation page
* for access to a protected resource.
*
* @author Ryan Heaton
*/
@Controller
@RequestMapping("/confirm_access")
public class AccessConfirmationController {
private ClientAuthenticationCache authenticationCache = new DefaultClientAuthenticationCache();
@Autowired
private ClientDetailsService clientDetailsService;
@RequestMapping(method = RequestMethod.GET)
protected ModelAndView confirm(HttpServletRequest request, HttpServletResponse response) throws Exception {
ClientAuthenticationToken clientAuth = authenticationCache.getAuthentication(request, response);
if (clientAuth == null) {
throw new IllegalStateException("No client authentication request to authorize.");
}
TreeMap<String, Object> model = new TreeMap<String, Object>();
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
model.put("auth_request", clientAuth);
model.put("client", client);
return new ModelAndView("access_confirmation", model);
}
}
Этот контроллер делегирует src / main / webapp / access_confirmation.jsp . Я создал этот файл и наполнил его кодом для отображения кнопок «Принять» и «Запретить».
<%@ page import="org.springframework.security.core.AuthenticationException" %>
<%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException" %>
<%@ page import="org.springframework.security.oauth2.provider.verification.BasicUserApprovalFilter" %>
<%@ page import="org.springframework.security.oauth2.provider.verification.VerificationCodeFilter" %>
<%@ page import="org.springframework.security.web.WebAttributes" %>
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Confirm Access</title>
<link rel="stylesheet" type="text/css" media="all"
href="http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css"/>
<style type="text/css">
h1 {
margin-left: -300px;
margin-top: 50px
}
</style>
</head>
<body>
<h1>Confirm Access</h1>
<div id="content">
<% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null &&
!(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %>
<div class="error">
<h2>Woops!</h2>
<p>Access could not be granted.
(<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>)</p>
</div>
<% } %>
<c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION"/>
<authz:authorize ifAnyGranted="ROLE_USER,ROLE_ADMIN">
<h2>Please Confirm</h2>
<p>You hereby authorize "<c:out value="${client.clientId}" escapeXml="true"/>" to access your protected resources.</p>
<form id="confirmationForm" name="confirmationForm"
action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="POST">
<input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>"
value="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/>
<label><input name="authorize" value="Authorize" type="submit"></label>
</form>
<form id="denialForm" name="denialForm"
action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="POST">
<input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>"
value="not_<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/>
<label><input name="deny" value="Deny" type="submit"></label>
</form>
</authz:authorize>
</div>
</body>
</html>
Наконец, мне нужно было создать src / main / webapp / login.jsp, чтобы пользователи могли войти в систему.
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Login</title>
<link rel="stylesheet" type="text/css" media="all"
href="http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css"/>
<style type="text/css">
h1 {
margin-left: -300px;
margin-top: 50px
}
</style>
</head>
<body>
<h1>Login</h1>
<form method="post" id="loginForm" action="<c:url value='/j_security_check'/>">
<fieldset style="padding-bottom: 0">
<ul>
<c:if test="${param.error != null}">
<li class="error">
${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
</li>
</c:if>
<li>
<label for="j_username" class="required desc">
Username <span class="req">*</span>
</label>
<input type="text" class="text medium" name="j_username"
id="j_username" tabindex="1"/>
</li>
<li>
<label for="j_password" class="required desc">
Password <span class="req">*</span>
</label>
<input type="password" class="text medium" name="j_password"
id="j_password" tabindex="2"/>
</li>
<li>
<input type="submit" class="button" name="login" value="Login"
tabindex="3"/>
</li>
</ul>
</fieldset>
</form>
</body>
</html>
Все изменения, описанные в приведенном выше разделе, необходимы для реализации OAuth, если вы создаете проект с помощью AppFuse WS 2.1. Это может показаться большим количеством кода, но я смог скопировать / вставить и заставить все это работать в приложении менее чем за 5 минут. Надеюсь, вы можете сделать то же самое. Я также планирую добавить его по умолчанию в следующую версию AppFuse. Теперь давайте посмотрим на интеграцию OAuth в клиент для аутентификации и извлечения данных из этого приложения.
Аутентификация и получение данных с клиентом.
Я изначально думал, что мое приложение GWT OAuth будет хорошим клиентом. Однако после 30 минут попыток заставить GWT 1.7.1 и плагин GWT Maven (1.1) работать с моим 64-битным Java 6 JDK на OS X я отказался. Поэтому я решил использовать приложение Ajax Login, которое я использовал в своих последних уроках по безопасности.
В этом примере я использовал OAuth2RestTemplate из Spring Security OAuth. Хотя это работает и работает хорошо, я все же хотел бы заставить вещи работать с GWT (или jQuery), чтобы продемонстрировать, как это сделать с чисто клиентской точки зрения.
Для начала я получил последний источник Ajax Login от GitHub (по состоянию на это утро) и внес некоторые изменения. Прежде всего, я добавил OAuth-зависимости Spring Security в pom.xml:
<repository>
<id>spring-milestone</id>
<url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
</repository>
...
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth</artifactId>
<version>1.0.0.M3</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
Затем я изменил src / main / webapp / WEB-INF / security.xml, добавил службу токенов OAuth и определил местоположение сервера OAuth.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd">
...
<oauth:client token-services-ref="oauth2TokenServices"/>
<beans:bean id="oauth2TokenServices"
class="org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices"/>
<oauth:resource id="appfuse" type="authorization_code" clientId="ajax-login"
accessTokenUri="http://localhost:9000/appfuse-oauth/oauth/authorize"
userAuthorizationUri="http://localhost:9000/appfuse-oauth/oauth/user/authorize"/>
Затем я создал контроллер, который использует OAuth2RestTemplate для выполнения запроса и получения данных из API приложения AppFuse OAuth. Я создал src / main / java / org / appfuse / examples / webapp / oauth / UsersApiController.java и заполнил его следующим кодом:
package org.appfuse.examples.webapp.oauth;
import org.appfuse.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.consumer.*;
import org.springframework.security.oauth2.consumer.token.OAuth2ClientTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
@RequestMapping("/appfuse/users")
@Controller
public class UsersApiController {
private OAuth2RestTemplate apiRestTemplate;
@Autowired
private OAuth2ClientTokenServices tokenServices;
private static final String REMOTE_DATA_URL = "http://localhost:9000/appfuse-oauth/api/users";
@Autowired
public UsersApiController(OAuth2ProtectedResourceDetails resourceDetails) {
this.apiRestTemplate = new OAuth2RestTemplate(resourceDetails);
}
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<User> getUsers() {
try {
List users = apiRestTemplate.getForObject(REMOTE_DATA_URL, List.class);
return new ArrayList<User>(users);
} catch (InvalidTokenException badToken) {
//we've got a bad token, probably because it's expired.
OAuth2ProtectedResourceDetails resource = apiRestTemplate.getResource();
OAuth2SecurityContext context = OAuth2SecurityContextHolder.getContext();
if (context != null) {
// this one is kind of a hack for this application
// the problem is that the sparklr photos page doesn't remove the 'code=' request parameter.
((OAuth2SecurityContextImpl) context).setVerificationCode(null);
}
//clear any stored access tokens...
tokenServices.removeToken(SecurityContextHolder.getContext().getAuthentication(), resource);
//go get a new access token...
throw new OAuth2AccessTokenRequiredException(resource);
}
}
}
В этот момент я думал, что все будет работать, и потратил довольно много времени, стуча головой о стену, когда это не сработало. Когда я составлял письмо в список рассылки Enunciate users, я понял проблему. Похоже, что он работает, но со стороны сервера, и перенаправления обратно к клиенту не происходит. Приложение Ajax Login использует UrlRewriteFilter (для красивых URL-адресов) для перенаправления с / app / * на / $ 1, и это перенаправление теряло параметр кода в URL-адресе.
<rule>
<from>/app/**</from>
<to last="true" type="redirect">%{context-path}/$1</to>
</rule>
Чтобы это исправить, я добавил use-query-string = «true» к корневому элементу в src / main / webapp / WEB-INF / urlrewrite.xml :
<urlrewrite default-match-type="wildcard" use-query-string="true">
После внесения всех этих изменений я запустил mvn jetty: run для обоих приложений и открыл http: // localhost: 8080 / appfuse / users в моем браузере. Все сработало, и на моем лице появилась улыбка. Я зарегистрировал изменения клиента в ajax-login на GitHub и пример appfuse-oauth в демонстрации AppFuse в Google Code . Если вы хотите увидеть этот пример в действии, я рекомендую вам ознакомиться с обоими проектами и сообщить мне, если вы обнаружите какие-либо проблемы.
От http://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and
