Skip to content

组织授权与审计平台对接文档

文档说明

本文档将指导您快速接入“组织授权与审计平台”,以快速获得平台用户登录能力,资源整合能力。本文档分为三个部分,分别包括原理说明,对接操作,相关资源。

文档版本v1.3

  • v1.3 修正一些标识符的写法
  • v1.2 提供springboot,shiro-spring-boot对接方案
  • v1.1 相关例子完善
  • v1.0 初版撰写

目录

一、技术原理

1.1 基于OAuth2.0协议实现的开放认证

OAuth2.0协议是目前互联网上使用最广泛的授权协议。该协议被广泛应用于腾讯开放平台,微信开放平台,支付宝开放平台,微博开放平台等,是经历过市场实践和考验的标准化协议。

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

其中,授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动,全程不会把通信暴露在前端,我们采用的就是这种模式

我们的服务端基于Spring Cloud微服务架构,采用Spring Security实现鉴权,以Spring Security OAuth2为基础进行高度定制化开发,实现了系统与系统之间的客户端授权模式,授权平台与网关服务的密码模式,以及认证服务器和第三方应用的授权码模式

1.2 基于Filter实现的开放认证客户端

Spring的OAuth2实现提供了很优秀的SDK,但是高度依赖Spring Security,且不利于我们的业务定制开发,所以我们自己开发了一套完整的客户端授权封装。这就是OAuth Client。OAuth Client经过几次迭代已经趋于稳定,可以在线上稳定的运行。

环境和标准

目前我们只提供了Java客户端,基于标准的J2EE模式进行封装,依赖Servlet的App容器得以运作。暂时不支持基于NIO模型运行的容器,如Netty

侵入性和系统影响

为了不影响贵系统的现有业务,我们使用Filter的方式无侵入的拦截OAuth的认证请求,不影响原功能的URL,所以请放心使用。 对接时,请依据您的环境,选择合适的高级封装SDK快速接入。我们根据常用的鉴权框架和应用框架,提供了各种组件。

几个内置的uri需要注意:

  • ${您的网站}/oauth/callback : 这是内置Servlet提供的回调地址。用户先访问系统,跳转到认证中心后, 将会作为回调地址,由SDK接受参数并自动获取token。
  • ${认证中心}/login : 这是认证中心的登录地址

全局过滤器Filter(由SDK提供)

SDK提供了默认的过滤器,会对贵方的服务端请求进行全量过滤,原理是通过判断token的有效性来决定放行, 如果token不存在或者无效,将会自动重定向到授权页面,交给用户授权登录。这些均只需要托管即可

1.3 数据同步原理

为了让贵系统的用户能够纳入统一认证中心,我们需要定时同步您的用户数据。

同步用户数据的操作不需要您进行实现,我们会定时的,主动的,以安全的加密模式拉取您的用户。这部分功能的侵入性非常低,对于接入而言,您只需要告诉我们用户是怎么查询的,还有您的用户信息是如何映射到我们的统一数据模型中即可。

平台服务器使用PBE算法(Password Based Encryption,基于口令加密)进行加密,内部的口令使用ClientSecret,以随机数列保证秘钥随机性,所有加解密过程都在服务器完成,最终使用REST的方式进行发送和接收。

二、快速接入指南

前置工作

申请您的ClientId和ClientSecret

请确认您对接的系统已经在认证中心注册,需要提供:

  • 外网可访问的地址

  • 您的系统id(建议自定义,如敏感,建议使用Base64编码) 在此步结束后,您应该已经拥有两个字段:

    ClientId、ClientSecret

    这两个字段非常重要,是系统对接的关键。

确认您的运行环境

SDK原生支持使用Maven或Gradle构建的项目,如果您的项目未使用依赖管理,我们将提供 lib依赖包打包下载。

对于Spring环境,您需要JDK1.7及以上版本。Spring环境经过原生封装,有很好的的接入体验。

其他的应用框架未原生支持,但是都支持接入,接入过程要比上述框架繁杂,望知悉。

2.1 SpringBoot集成

如果您的系统基于SpringBoot实现,您需要定义配置,实现相关转换逻辑,并提供用户查询方法。您可以通过以下三步快速接入:

2.1.1 运行环境和准备工作

  1. 请升级您的开发环境,保证至少为 JDK 1.8(Java 8)版本,低版本不支持
  2. 请确保项目引用的Servlet API版本为3.0以上。建议使用至少如tomcat8作为开发容器
  3. SDK 的 JavaDoc 地址:

您可以使用Maven或者Gradle构建您的项目,依赖如下:

Maven依赖

我们在Maven全球中央仓库发布了Maven依赖,请直接引入以快速接入我们的SDK。目前最新版本:1.0.7

xml
<dependencies>
  <dependency>
    <groupId>group.flyfish.oauth</groupId>
    <artifactId>oauth-spring-boot</artifactId>
    <version>1.0.7</version>
  </dependency>
</dependencies>
Gradle依赖
nginx
dependencies {
    compile "group.flyfish.oauth:oauth-spring-boot:1.0.7"
}

2.1.2 配置您的OAuth客户端

如果您使用SpringBoot,SDK默认会检测您classpath下的oauth.yml作为默认配置文件,您也可以在spring默认的配置文件中注入配置,如application.propertiesapplication.yml

配置看起来像这样:

yaml
oauth:
  client:
    client-id: my-app                                   # 您申请的clientId
    client-secret: $1$RuldJVuF$nca1fNnjHQaHCRpMYcc.p1		# 您申请的clientSecret
    local-url: http://60.221.255.208:8087								# 您系统的外网访问地址
    server-url: http://220.194.160.2:8088									# 组织授权服务端地址
    redirect-uri: http://60.221.255.208:8087/oauth/callback  # 非必需,您的外网回调地址
    user-authorization-uri: http://60.221.255.204:8088/login # 非必需,组织授权服务端登录地址,需要内外网转换必需

大多数情况下,您的配置文件只需要包含:

yaml
oauth:
  client:
    client-id: my-app
    client-secret: $1$MU1BZm/Y$Lg.zzRk35VYpqk8.IcWSM0
    local-url: http://60.221.255.208:8087
    server-url: http://220.194.160.2:8088

除了设置OAuth客户端配置信息之外,您还需要排除 /oauth/** 路径下的鉴权判定,因为该路径被SDK用来做授权处理,很多问题都是因为没有排除鉴权导致的。

2.1.3 使用配置类实现您的逻辑

2.1.3.1 使用注解启用默认的配置扫描

请在您的启动类上添加@SpringBootOAuth注解以启用配置扫描。

2.1.3.2 实现相关配置类

实现配置类,以添加Session登录态转换,用户模型转换,用户同步相关能力对接。您有两种选择:

选择一 【推荐】添加OAuthConfig配置类,使用@Bean实现相关配置类

您可以方便的用@Bean实现相关配置类,所有的实现都统一管理,便于维护,还能方便的在方法参数注入需要的查询Service。

下面是demo中附带的实现源码,仅供参考。在您实现的时候,请注意替换相关泛型和转换逻辑为您自己系统的逻辑

java
import ...

@Configuration
public class OAuthConfig {

    /**
     * SSO会话转换器,提供登录态转换
     *
     * @return 实例化的内部类
     */
    @Bean
    public SSOSessionConverter<User> ssoSessionConverter() {
        return new SSOSessionConverter<User>() {
            @Override
            public boolean convert(HttpSession session, User userInfo) {
                if (null == userInfo) {
                    // 用户未成功转换,session转换失败
                    return false;
                }
                // 简单的设置一个属性,标识已经登录
                session.setAttribute("user", userInfo);
                return true;
            }

            @Override
            public boolean isComplete(HttpSession session) {
                // 判断用户是否已经鉴权过,返回系统内部的登录态
                return session.getAttribute("user") != null;
            }

            @Override
            public String expectRedirectUri(HttpServletRequest request) {
                // 期望的跳转路径。如果您想要在认证成功后跳转特定路径,请填写uri。如无必要,请保持留空
                // 注意,如果授权中心未配置合法跳转url,这里定义的url可能会无效,建议配置的地址和平台注册客户端的首页地址一致
                return null;
            }
        };
    }

    /**
     * 用户同步提供者,提供用户列表查询和用户信息转换
     *
     * @return 实例化的bean
     */
    @Bean
    public SyncUserProvider<User> syncUserProvider(UserService userService, DepartService departService) {
        return new AbstractUserProvider<User>() {
            @Override
            protected LocalUser transform(User domain) {
                // 转换本地用户到统一的用户结构
                return LocalUser.builder()
                        // 关键的outerId,必需包含,作为系统对接的依据,内容为本地用户的id
                        .outerId(String.valueOf(domain.getId()))
                        .name(domain.getName())
                        .comment(domain.getSign())
                        .phone(domain.getPhone())
                        .username(domain.getUsername())
                        .organization(Optional.ofNullable(domain.getDepartIds()).map(departIds -> departIds
                                .stream()
                                .map(departService::getById)
                                .map(Depart::getFullName)
                                .collect(Collectors.toList())
                        ).orElse(Collections.emptyList()))
                        .build();
            }

            @Override
            protected List<User> getLocalUsers() {
                // 查询本地用户(全部可分享用户)
                return userService.getByFilter(User.create());
            }
        };
    }

    /**
     * 用户转换器,提供用户查询和转换
     *
     * @return 实例化的bean
     */
    @Bean
    public UserConverter<User> userConverter(UserService userService) {
        // 直接返回用户转换逻辑,从封装的用户实体中利用outerId获取本地用户实体
        return new UserConverter<User>() {
            @Override
            public User convert(LocalUser localUser) {
                return userService.getById(Long.valueOf(localUser.getOuterId()));
            }
        };
    }
}
选择二 实现相关配置类,而不是使用@Bean

相对的,如果您不喜欢使用@Bean的方式实现,您可以单独实现以下接口或抽象类,并用@Component@Service标注他们。

  • com.flyfish.oauth.configuration.OAuth2SsoUserService<T>
  • com.flyfish.oauth.configuration.SSOSessionConverter<T>
  • com.flyfish.oauth.configuration.sync.user.AbstractUserProvider<T>

其中的泛型 <T> 都是您系统中实际的用户类型

2.1.3.3 在授权中心对用户进行授权,测试

您可以登录组织授权与审计平台,在用户授权管理模块对您的系统用户进行授权测试。届时会为您开放权限。

对接开始后,会陆续维护FAQ,以解决问题。

2.2 SpringMVC集成

SpringMVC提供了简单的封装类,对配置实体和配置加载进行了简单的封装,您可以引入以下依赖:

xml
<dependency>
  <groupId>group.flyfish.oauth</groupId>
  <artifactId>oauth-spring</artifactId>
  <version>1.0.7</version>
</dependency>

以下提供两种方案供大家参考。

准备工作需要您确认jdk>=1.7.0,spring4.3以上。

2.2.1 【推荐】使用注解集成

2.2.1.1 开启配置类扫描

请确认已经在web.xml中添加您的配置类扫描。如果没有指定,请指定到您项目的config包下,如:

xml
 <servlet>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   			<!-- 使用基于注解的上下文配置,不必要请不要改动您自己的,如果您使用的是xml的话 -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <!-- 添加配置类路径,默认扫描,最先加载 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.xxx.xxxx.config</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
2.2.1.2 开启注解驱动

首先请在您的spring.xml中开启注解驱动,使用配置类或者已经开启的请忽略。

xml
<mvc:annotation-driven />
2.2.1.3 配置您的客户端

请在您的资源目录下添加配置文件以快速配置客户端。如放到src/main/resources/oauth.properties下。

properties
# 是否开启审计功能
oauth.client.audit=true
# 您申请的clientId
#oauth.client.clientId=flyfish
oauth.client.clientId=test
# 您申请的clientSecret
#oauth.client.clientSecret=$1$q0rj6rbB$sjipzTV4gnjSRBfvoIkW0/
oauth.client.clientSecret=$1$c4IOwjVj$vVqRq9zQqMaGK5lMoh01h1
# 您的本地外网可访问的url
oauth.client.localUrl=http://localhost:8080
# 组织授权服务器地址
#oauth.client.serverUrl=http://localhost:8000
oauth.client.serverUrl=http://220.194.160.6:8088
# 授权回调地址,默认,不需要改
oauth.client.redirectUri=/oauth/callback
# 用户鉴权uri,默认,不需要改
oauth.client.userAuthorizationUri=/login
# 用户登出的uri,默认,不需要改
oauth.client.userLogoutUri=/logout
# 是否总是自动跳转,建议false,否则将无法使用原登录
oauth.client.autoRedirect=false
# 允许的url,标识平台鉴权过滤器放行的url,根据实际情况添加
oauth.client.allowUris=/authenticate,login.jsp
2.2.1.3 创建配置类

使用原生的@Configuration配置类创建。请参考SpringBoot的相关配置,更方便的,请查看demo。

请在这里注入您的配置文件添加配置启用注解@EnableSpringOAuth,实现用户转换服务OAuth2SsoUserService,实现用户登录态转换器SSOSessionConverter,实现用户同步方法SyncUserProvider中的transformgetLocalUsers。具体的请查看代码示例。

请在2.2.1.1小节中指定的配置路径下建立OAuthConfig.java

java
/**
 * 开放认证配置类
 * 本案例为了便于实现,使用注解驱动,配置文件位于oauth.properties中
 *
 * @author wangyu
 */
@Configuration
@EnableSpringOAuth
public class OAuthConfig {

    /**
     * 便捷的注入配置
     *
     * @return 结果
     * @throws IllegalAccessException    异常访问异常
     * @throws IOException               io异常
     * @throws InvocationTargetException 执行目标异常
     */
    @Bean
    public OAuthProperties oAuthProperties() throws IllegalAccessException, IOException, InvocationTargetException {
        // 加载您存放配置文件的位置。已经为您封装内部的配置加载逻辑,无须修改就能调用。
        return OAuthProperties.load("/oauth.properties");
    }

    /**
     * 使用默认的用户服务包裹转换器,提供用户查询和转换
     *
     * @return 实例化的bean
     */
    @Bean
    public OAuth2SsoUserService<User> ssoUserService(final UserService userService) {
        // 您需要实现从我们提供的LocalUser对象转换为您自己的用户对象。本案例中使用数据库查询获取最新的本地用户模型对象。
        return new DefaultUserService<>(new UserConverter<User>() {
            @Override
            public User convert(LocalUser localUser) {
                return userService.getById(Long.valueOf(localUser.getOuterId()));
            }
        });
    }
  
    /**
     * SSO会话转换器,提供登录态转换
     *
     * @return 实例化的内部类
     */
    @Bean
    public SSOSessionConverter<User> ssoSessionConverter() {
        return new SSOSessionConverter<User>() {
            /**
             * 转换session状态,请根据您自己的鉴权逻辑,模拟用户登录时的鉴权过程,获得鉴权状态
             *
             * @param session  会话
             * @param userInfo 用户信息
             * @return 结果,成功与否
             */
            @Override
            public boolean convert(HttpSession session, User userInfo) {
                if (null == userInfo) {
                    // 用户未成功转换,session转换失败
                    return false;
                }
                // 简单的设置一个属性,标识已经登录
                session.setAttribute("user", userInfo);
                return true;
            }

            @Override
            public boolean isComplete(HttpSession session) {
                // 判断用户是否已经鉴权过,返回系统内部的登录态
                return session.getAttribute("user") != null;
            }

            @Override
            public String expectRedirectUri(HttpServletRequest request) {
                // 期望的跳转路径。如果您想要在认证成功后跳转特定路径,请填写uri。如无必要,请保持留空
                // 注意,如果授权中心未配置合法跳转url,这里定义的url可能会无效,建议配置的地址和平台注册客户端的首页地址一致
                return null;
            }
        };
    }

    /**
     * 用户同步提供者,提供用户列表查询和用户信息转换
     *
     * @return 实例化的bean
     */
    @Bean
    public SyncUserProvider<User> syncUserProvider(final UserService userService, final DepartService departService) {
        return new AbstractUserProvider<User>() {
            @Override
            protected LocalUser transform(User domain) {
                List<String> organizations = new ArrayList<>();
                List<Long> departIds = domain.getDepartIds();
                if (CollectionUtils.isNotEmpty(departIds)) {
                    for (Long departId : departIds) {
                        Depart depart = departService.getById(departId);
                        if (null != depart) {
                            organizations.add(depart.getFullName());
                        }
                    }
                }
                // 转换本地用户到统一的用户结构
                return LocalUser.builder()
                        // 关键的outerId,必需包含,作为系统对接的依据,内容为本地用户的id
                        .outerId(String.valueOf(domain.getId()))
                        .name(domain.getName())
                        .comment(domain.getSign())
                        .phone(domain.getPhone())
                        .username(domain.getUsername())
                        .organization(organizations)
                        .build();
            }

            @Override
            protected List<User> getLocalUsers() {
                // 查询本地用户(全部可分享用户)
                return userService.getByFilter(User.create());
            }
        };
    }

}
2.2.1.4 测试接入成果

您可以登录组织授权与审计平台,在用户授权管理模块对您的系统用户进行授权测试。届时会为您开放权限。

对接开始后,会陆续维护FAQ,以解决问题。

2.2.2 使用XML集成

除了使用注解的方式,您还可以使用xml。使用xml未提供例子,这里先提供您注入配置的方式,仅供参考。

使用xml实现配置信息。如下:

xml-dtd
<!--声明以注解的方式配置spring -->
<mvc:annotation-driven />
<!--引入配置文件-->
<context:property-placeholder location="classpath:oauth.properties"/>

<!--引入spring 和shiro集成的主配置文件-->
<bean id="oauthProperties" class="com.flyfish.oauth.spring.properties.OAuthProperties">
  <property name="clientId" value="${oauth.client.clientId}" />
  <property name="clientSecret" value="${oauth.client.clientSecret}" />
  <property name="localUrl" value="${oauth.client.localUrl}" />
  <property name="serverUrl" value="${oauth.client.serverUrl}" />
  <property name="allowUris" value="${oauth.client.allowUris}" />
  <property name="autoRedirect" value="${oauth.client.autoRedirect}" />
  <property name="redirectUri" value="${oauth.client.redirectUri}" />
  <property name="userAuthorizationUri" value="${oauth.client.userAuthorizationUri}" />
  <property name="userLogoutUri" value="${oauth.client.userLogoutUri}" />
</bean>

2.3 与shiro+springboot框架集成

如果您的系统使用shiro作为鉴权框架,那么对接起来将会事半功倍。我们提供了专门用于shiro的sdk,能够利用spring的注解式配置完成所有的对接事宜。

2.3.1 准备工作和系统需求

环境要求

JDK版本:JDK1.8及以上

依赖管理:Maven或者Gradle

框架依赖:Spring MVC 4.3以上,Spring Boot 2.0及以上,Shiro非早期版本(建议1.6.0,漏洞少)

容器类型:基于Servlet的Java应用容器,如TomcatJetty等。

Maven依赖

我们在Maven全球中央仓库发布了Maven依赖,请直接引入以快速接入我们的SDK。目前最新版本:1.0.7。

xml
<dependencies>
  <dependency>
    <groupId>group.flyfish.oauth</groupId>
    <artifactId>oauth-shiro-support</artifactId>
    <version>1.0.7</version>
  </dependency>
</dependencies>
Gradle依赖
nginx
dependencies {
    compile "group.flyfish.oauth:oauth-shiro-support:1.0.7"
}

如果遇到依赖无法下载的问题,请删除缓存再试。

2.3.2 在启动类上引入配置

SpringBoot

如果您的项目基于SpringBoot,那么需要在启动类上添加@EnableShiroOAuth以开启配置扫描。

Spring MVC

如果您的项目未基于SpringBoot,您需要手动创建@Configuration类,并在该类上添加@EnableShiroOAuth

当然,对于非注解扫描的项目,请在xml里声明我们的配置类 com.flyfish.oauth.shiro.ShiroOAuthConfiguration

强烈建议开启mvc:annotation-driven以获得最大便利。

2.3.3 创建OAuthConfig配置类

为了快速接入,我们已经为您实现了大多数的必需接口。为了完成一些个性化的转换过程,需要实现以下几个interface以完成接入。推荐您使用Spring的@Configutaion配置类,结合@Bean快速实现,集中配置。

2.3.3.1 定义UserConverter,转换用户体系

用户转换器UserConverter。sdk已经帮助实现了用户服务以及与shiro的相关交互逻辑,所以您只需要实现用户转换逻辑即可。用户转换器从平台的用户模型转换为您的实际用户模型。

2.3.3.2 定义用户提供者,提供用户同步

用户提供者SyncUserProvider是用户同步的重要实现,需要您提供用户的转换,用户的查询两部分。

transform方法需要您将您的用户模型转换为组织授权平台可存储的用户模型(localUser)

getLocalUsers方法则是查询贵系统对组织授权平台可见的所有用户,查询出来的用户将由sdk组装并发送给服务器。

2.3.3.3 定义鉴权抽取器,从平台响应中抽取需要的内容

鉴权抽取器OAuthAuthorizationExtractor,需要您实现:

  1. 鉴权信息AuthorizationInfo的抽取方法extract
  2. 用户身份信息的抽取方法extractPrincipal
  3. 用户从组织授权平台登录成功后的回调authenticateSuccess(可做原系统登录后的额外处理)
2.3.3.4 代码范例
java
@Configuration
public class OAuthConfig {

    /**
     * 用户转换器,从关联信息取出
     *
     * @return 结果
     */
    @Bean
    public UserConverter<User> userConverter(UserService userService) {
        return localUser -> userService.getById(localUser.getOuterId());
    }

    /**
     * 用户提供者,用于系统同步
     *
     * @param userService 用户服务
     * @param deptService 部门服务
     * @return 结果
     */
    @Bean
    public SyncUserProvider<User> userProvider(UserService userService, DeptService deptService) {
        return new AbstractUserProvider<User>() {

            @Override
            protected LocalUser transform(User domain) {
                return LocalUser.builder()
                        .outerId(String.valueOf(domain.getUserId()))
                        .username(domain.getAccount())
                        .phone(domain.getPhone())
                        .email(domain.getEmail())
                        .birthDay(domain.getBirthday())
                        .organization(Optional.ofNullable(domain.getDeptId())
                                .map(deptService::getById)
                                .map(Dept::getSimpleName)
                                .map(Arrays::asList)
                                .orElse(Collections.emptyList())
                        )
                        .gender("M".equals(domain.getSex()) ? Gender.MALE : Gender.FEMALE)
                        .name(domain.getName())
                        .build();
            }

            @Override
            protected List<User> getLocalUsers() {
                return userService.list();
            }
        };
    }


    /**
     * shiro的鉴权解压
     *
     * @return 结果
     */
    @Bean
    public OAuthAuthorizationExtractor<User> authAuthorizationExtractor(JwtTokenApplier jwtTokenApplier) {
        return new OAuthAuthorizationExtractor<User>() {

            @Override
            public AuthorizationInfo extract(PrincipalCollection principals) {
                return AuthorizeUtils.getAuthInfo((ShiroUser) principals.getPrimaryPrincipal());
            }

            @Override
            public Object extractPrincipal(User user) {
                UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
                return shiroFactory.shiroUser(user);
            }

            /**
             * 模仿登录控制器,完成剩余工作
             * @param user 用户
             */
            @Override
            public void authenticateSuccess(User user) {
                RequestContext.getRequestResponse().ifPresent(((request, response) -> {
                    // 登录成功,将token放入Cookie,共享给bpm
                    String token = jwtTokenApplier.generate(user.getAccount());
                    jwtTokenApplier.apply(token, request, response);
                    ShiroKit.getSession().setAttribute("sessionFlag", true);
                }));
            }
        };
    }

}

2.4 非Spring集成(J2EE)

本SDK额外提供了非Spring环境的对接解决demo,详细请下载demo查看,建议对接前先通读本文档。

三、部署和调试

由于OAuth2协议涉及到浏览器跳转请求,对于一般的j2ee项目来说没有任何问题 但是对于前后端完全分离的项目而言,需要额外的做一部分工作。

前后端分离下,前端通过ajax访问后端,对于应用而言,无法完成browser request。 我们使用曲线救国的方式,匹配特征和路径都放到web中间件去做

以下是本地化webpack代理配置

javascript
const devServer = {
  index: '',  // 重置index以代理根路径
  ...,
  proxy: {
    '/': {
      target: 'http://localhost:8089',
      changeOrigin: true,
      bypass: function(req, res, proxyOptions) {
        if (!req.query.oauth) {
          return '/index.html';
        }
      },
    },
    '/oauth': {
        target: 'http://localhost:8089',
        ws: false,
        changeOrigin: true,
    },
  }
};

以下是nginx参考配置

editorconfig
server {
    ...

    location / {
        if ( $query_string ~* "oauth=1" ) {
            # 当且仅当包含鉴权标记,才跳转
            proxy_pass      http://localhost:8089;
        }
        root   ...;
        try_files  $uri $uri/ /index.html;
        index  index.html index.htm;
    }

    # 代理鉴权filter路径
    location /oauth {
        proxy_pass http://localhost:8089/;
        proxy_set_header Host $host;
        proxy_buffer_size  128k;
        proxy_buffers   32 32k;
        proxy_busy_buffers_size 128k;
    }

}

四、demo和下载支持

Demo 下载地址