前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Shiro 拓展之 Restful URL 鉴权

Shiro 拓展之 Restful URL 鉴权

作者头像
一份执着✘
发布于 2018-12-14 09:18:52
发布于 2018-12-14 09:18:52
2.1K00
代码可运行
举报
文章被收录于专栏:赵俊的Java专栏赵俊的Java专栏
运行总次数:0
代码可运行

前言

在使用 Shiro 的过程中,遇到一个痛点,就是对 restful 支持不太好,也查了很多资料,各种各样的方法都有,要不就是功能不完整,要不就是解释不清楚,还有一些对原有功能的侵入性太强,经过一番探索,算是最简的配置下完成了需要的功能,这里给大家分享下。大家如果又更好的方案,也可以在评论区留言,互相探讨下。

虽然深入到了源码进行分析,但过程并不复杂,希望大家可以跟着我的思路捋顺了耐心看下去,而不是看见源码贴就抵触。

分析

首先先回顾下 Shiro 的过滤器链,一般我们都有如下配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/login.html = anon
/login = anon
/users = perms[user:list]
/** = authc

不太熟悉的朋友可以了解下这篇文章:Shiro 过滤器

其中 /users 请求对应到 perms 过滤器,对应的类: org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter,其中的 onAccessDenied 方法是在没有权限时被调用的, 源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {

    Subject subject = getSubject(request, response);
    // 如果未登录, 则重定向到配置的 loginUrl
    if (subject.getPrincipal() == null) {
        saveRequestAndRedirectToLogin(request, response);
    } else {
        // 如果当前用户没有权限, 则跳转到 UnauthorizedUrl
        // 如果没有配置 UnauthorizedUrl, 则返回 401 状态码.
        String unauthorizedUrl = getUnauthorizedUrl();
        if (StringUtils.hasText(unauthorizedUrl)) {
            WebUtils.issueRedirect(request, response, unauthorizedUrl);
        } else {
            WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }
    }
    return false;
}

我们可以在这里可以判断当前请求是否时 AJAX 请求,如果是,则不跳转到 logoUrl 或 UnauthorizedUrl 页面,而是返回 JSON 数据。

还有一个方法是 pathsMatch,是将当前请求的 url 与所有配置的 perms 过滤器链进行匹配,是则进行权限检查,不是则接着与下一个过滤器链进行匹配,源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected boolean pathsMatch(String path, ServletRequest request) {
    String requestURI = getPathWithinApplication(request);
    log.trace("Attempting to match pattern '{}' with current requestURI '{}'...", path, requestURI);
    return pathsMatch(path, requestURI);
}

方法

了解完这两个方法,我来说说如何利用这两个方法来实现功能。

我们可以从配置的过滤器链来入手,原先的配置如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/users = perms[user:list]

我们可以改为 /user==GET/user==POST 方式。== 用来分隔, 后面的部分指 HTTP Method

使用这种方式还要注意一个方法,即:org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver 中的 getChain 方法,用来获取当前请求的 URL 应该使用的过滤器,源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
    // 1. 判断有没有配置过滤器链, 没有一个过滤器都没有则直接返回 null
    FilterChainManager filterChainManager = getFilterChainManager();
    if (!filterChainManager.hasChains()) {
        return null;
    }

    // 2. 获取当前请求的 URL
    String requestURI = getPathWithinApplication(request);

    // 3. 遍历所有的过滤器链
    for (String pathPattern : filterChainManager.getChainNames()) {

        // 4. 判断当前请求的 URL 与过滤器链中的 URL 是否匹配.
        if (pathMatches(pathPattern, requestURI)) {
            if (log.isTraceEnabled()) {
                log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +
                        "Utilizing corresponding filter chain...");
            }
            // 5. 如果路径匹配, 则获取其实现类.(如 perms[user:list] 或 perms[user:delete] 都返回 perms)
            // 具体对  perms[user:list] 或 perms[user:delete] 的判断是在上面讲到的 PermissionsAuthorizationFilter 的 pathsMatch 方法中.
            return filterChainManager.proxy(originalChain, pathPattern);
        }
    }

    return null;
}

这里大家需要注意,第四步的判断,我们已经将过滤器链,也就是这里的 pathPattern 改为了 /xxx==GET 这种方式,而请求的 URL 却仅包含 /xxx,那么这里的 pathMatches 方法是肯定无法匹配成功,所以我们需要在第四步判断的时候,只判断前面的 URL 部分。

整个过程如下:

  1. 在过滤器链上对 restful 请求配置需要的 HTTP Method,如:/user==DELETE
  2. 修改 PathMatchingFilterChainResolvergetChain 方法,当前请求的 URL 与过滤器链匹配时,过滤器只取 URL 部分进行判断。
  3. 修改过滤器的 pathsMatch 方法,判断当前请求的 URL 与请求方式是否与过滤器链中配置的一致。
  4. 修改过滤器的 onAccessDenied 方法,当访问被拒绝时,根据普通请求和 AJAX 请求分别返回 HTMLJSON 数据。

下面我们逐步来实现:

实现

过滤器链添加 http method

在我的项目中是从数据库获取的过滤器链,所以有如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Map<String, String> getUrlPermsMap() {
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

    filterChainDefinitionMap.put("/favicon.ico", "anon");
    filterChainDefinitionMap.put("/css/**", "anon");
    filterChainDefinitionMap.put("/fonts/**", "anon");
    filterChainDefinitionMap.put("/images/**", "anon");
    filterChainDefinitionMap.put("/js/**", "anon");
    filterChainDefinitionMap.put("/lib/**", "anon");
    filterChainDefinitionMap.put("/login", "anon");

    List<Menu> menus = selectAll();
    for (Menu menu : menus) {
        String url = menu.getUrl();
        if (!"".equals(menu.getMethod())) {
            url += ("==" + menu.getMethod());
        }
        String perms = "perms[" + menu.getPerms() + "]";
        filterChainDefinitionMap.put(url, perms);
    }
    filterChainDefinitionMap.put("/**", "authc");
    return filterChainDefinitionMap;
}

如: /xxx==GET = perms[user:list]这里的 getUrlgetMethodgetPerms 分别对应 /xxxGETuser:list。 不过需要注意的是,如果在 XML 里配置,会被 Shiro 解析成 /xxx=GET = perms[user:list],解决办法是使用其他符号代替 ==

修改 PathMatchingFilterChainResolver 的 getChain 方法

由于 Shiro 没有提供相应的接口,且我们不能直接修改源码,所以我们需要新建一个类继承 PathMatchingFilterChainResolver 并重写 getChain 方法,然后替换掉 PathMatchingFilterChainResolver 即可。

首先继承并重写方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package im.zhaojun.shiro;

import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class RestPathMatchingFilterChainResolver extends PathMatchingFilterChainResolver {

    private static final Logger log = LoggerFactory.getLogger(RestPathMatchingFilterChainResolver.class);

    @Override
    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        FilterChainManager filterChainManager = getFilterChainManager();
        if (!filterChainManager.hasChains()) {
            return null;
        }

        String requestURI = getPathWithinApplication(request);

        //the 'chain names' in this implementation are actually path patterns defined by the user.  We just use them
        //as the chain name for the FilterChainManager's requirements
        for (String pathPattern : filterChainManager.getChainNames()) {

            String[] pathPatternArray = pathPattern.split("==");

            // 只用过滤器链的 URL 部分与请求的 URL 进行匹配
            if (pathMatches(pathPatternArray[0], requestURI)) {
                if (log.isTraceEnabled()) {
                    log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +
                            "Utilizing corresponding filter chain...");
                }
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }

        return null;
    }
}

然后替换掉 PathMatchingFilterChainResolver,它是在 ShiroFilterFactoryBeancreateInstance 方法里初始化的。

所以同样的套路,继承 ShiroFilterFactoryBean 并重写 createInstance 方法,将 new PathMatchingFilterChainResolver(); 改为 new RestPathMatchingFilterChainResolver(); 即可。

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package im.zhaojun.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanInitializationException;

public class RestShiroFilterFactoryBean extends ShiroFilterFactoryBean {

    private static final Logger log = LoggerFactory.getLogger(RestShiroFilterFactoryBean.class);

    @Override
    protected AbstractShiroFilter createInstance() {

        log.debug("Creating Shiro Filter instance.");

        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }

        FilterChainManager manager = createFilterChainManager();

        //Expose the constructed FilterChainManager by first wrapping it in a
        // FilterChainResolver implementation. The AbstractShiroFilter implementations
        // do not know about FilterChainManagers - only resolvers:
        PathMatchingFilterChainResolver chainResolver = new RestPathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
        //FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class
        //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
        //injection of the SecurityManager and FilterChainResolver:
        return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

    private static final class SpringShiroFilter extends AbstractShiroFilter {
        protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
            super();
            if (webSecurityManager == null) {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            }
            setSecurityManager(webSecurityManager);
            if (resolver != null) {
                setFilterChainResolver(resolver);
            }
        }
    }
}

最后记得将 ShiroFilterFactoryBean 改为 RestShiroFilterFactoryBean

XML 方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<bean id="shiroFilter" class="im.zhaojun.shiro.RestShiroFilterFactoryBean">
    <!-- 参数配置略 -->
</bean>

Bean 方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new RestShiroFilterFactoryBean();
    // 参数配置略
    return shiroFilterFactoryBean;
}

修改过滤器的 pathsMatch 方法

同样新建一个类继承原有的 PermissionsAuthorizationFilter 并重写 pathsMatch 方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package im.zhaojun.shiro.filter;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 修改后的 perms 过滤器, 添加对 AJAX 请求的支持.
 */
public class RestAuthorizationFilter extends PermissionsAuthorizationFilter {

    private static final Logger log = LoggerFactory
            .getLogger(RestAuthorizationFilter.class);

    @Override
    protected boolean pathsMatch(String path, ServletRequest request) {
        String requestURI = this.getPathWithinApplication(request);

        String[] strings = path.split("==");

        if (strings.length <= 1) {
            // 普通的 URL, 正常处理
            return this.pathsMatch(strings[0], requestURI);
        } else {
            // 获取当前请求的 http method.
            String httpMethod = WebUtils.toHttp(request).getMethod().toUpperCase();

            // 匹配当前请求的 http method 与 过滤器链中的的是否一致
            return httpMethod.equals(strings[1].toUpperCase()) && this.pathsMatch(strings[0], requestURI);
        }
    }
}

修改过滤器的 onAccessDenied 方法

同样是上一步的类,重写 onAccessDenied 方法即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 当没有权限被拦截时:
 *          如果是 AJAX 请求, 则返回 JSON 数据.
 *          如果是普通请求, 则跳转到配置 UnauthorizedUrl 页面.
 */
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
    Subject subject = getSubject(request, response);
    // 如果未登录
    if (subject.getPrincipal() == null) {
        // AJAX 请求返回 JSON
        if (im.zhaojun.util.WebUtils.isAjaxRequest(WebUtils.toHttp(request))) {
            if (log.isDebugEnabled()) {
                log.debug("用户: [{}] 请求 restful url : {}, 未登录被拦截.", subject.getPrincipal(), this.getPathWithinApplication(request));                }
            Map<String, Object> map = new HashMap<>();
            map.put("code", -1);
            im.zhaojun.util.WebUtils.writeJson(map, response);
        } else {
            // 其他请求跳转到登陆页面
            saveRequestAndRedirectToLogin(request, response);
        }
    } else {
        // 如果已登陆, 但没有权限
        // 对于 AJAX 请求返回 JSON
        if (im.zhaojun.util.WebUtils.isAjaxRequest(WebUtils.toHttp(request))) {
            if (log.isDebugEnabled()) {
                log.debug("用户: [{}] 请求 restful url : {}, 无权限被拦截.", subject.getPrincipal(), this.getPathWithinApplication(request));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("code", -2);
            map.put("msg", "没有权限啊!");
            im.zhaojun.util.WebUtils.writeJson(map, response);
        } else {
            // 对于普通请求, 跳转到配置的 UnauthorizedUrl 页面.
            // 如果未设置 UnauthorizedUrl, 则返回 401 状态码
            String unauthorizedUrl = getUnauthorizedUrl();
            if (StringUtils.hasText(unauthorizedUrl)) {
                WebUtils.issueRedirect(request, response, unauthorizedUrl);
            } else {
                WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
            }
        }

    }
    return false;
}

重写完 pathsMatchonAccessDenied 方法后,将这个类替换原有的 perms 过滤器的类:

XML 方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<bean id="shiroFilter" class="im.zhaojun.shiro.RestShiroFilterFactoryBean">
    <!-- 参数配置略 -->
    <property name="filters">
        <map>
            <entry key="perms" value-ref="restAuthorizationFilter"/>
        </map>
    </property>
</bean>

<bean id="restAuthorizationFilter" class="im.zhaojun.shiro.filter.RestAuthorizationFilter"/>

Bean 方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new RestShiroFilterFactoryBean();
    Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
    filters.put("perms", new RestAuthorizationFilter());

    // 其他配置略
    return shiroFilterFactoryBean;
}

这里只改了 perms 过滤器,对于其他过滤器也是同样的道理,重写过滤器的 pathsMatchonAccessDenied 方法,并覆盖原有过滤器即可。

结语

基本的过程就是这些,这是我在学习 Shiro 的过程中的一些见解,希望可以帮助到大家。具体应用的项目地址为:https://github.com/zhaojun1998/Shiro-Action,功能在不断完善中,代码可能有些粗糙,还请见谅。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-11-172,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
谢谢分享啦
谢谢分享啦
回复回复点赞举报
推荐阅读
nginx正向代理配置详解
nginx不仅可以做反向代理,还能用作正向代理来进行上网等功能。如果把局域网外的Internet想象成一个巨大的资源库,则局域网中的客户端要访问Internet,则需要通过代理服务器来访问,这种代理服务就称为正向代理(也就是大家常说的,通过正向代理进行上网功能)
随心助手
2019/10/15
58.4K0
基于CentOS 7配置Nginx正向代理
Nginx是一款以轻量级、低内存开销、支持缓存、支持反向代理,负载均衡,电子邮件服务而著称。对于鲜为人知的是,它还可以作为一个简单易用的正向代理服务器。
院长技术
2021/01/08
9450
基于CentOS 7配置Nginx正向代理
Nginx是一款以轻量级、低内存开销、支持缓存、支持反向代理,负载均衡,电子邮件服务而著称。对于鲜为人知的是,它还可以作为一个简单易用的正向代理服务器。本文简要描述这个正向代理功能并给出演示,供大家参考。
Leshami
2018/08/06
2.3K0
Nginx正向代理
记一次使用Nginx做正向代理,实现http与https的代理访问 1.环境介绍 操作系统:CentOS Linux release 7.6.1810 (Core) Nginx编译安装,版本-nginx/1.18.0 服务器A部署在DMZ区域且可上网,服务器B不可上网,但是和服务器A能够通信 在服务器A上部署Nginx正向代理,使服务器B通过服务器A正向代理功能可访问http和https Nginx默认不支持https的正向代理,这里需要安装第三方模块“ngx_http_proxy_connect_modu
染指流年
2023/03/13
9820
基于CentOS 7配置Nginx反向代理
Nginx作为反向代理服务器被广泛使用在各大互联网企业。它简单易用,可以根据业务的需求将其不同的业务类型代理至不同的服务器,将整个站点请求压力按类型分摊到不同的服务器。该方式使的整个站点请求性能得以极大的提升。本文简要描述了Nginx几种不同情形的代理演示,供大家参考。
Leshami
2018/08/06
2.2K0
基于CentOS 7配置Nginx反向代理
CDN-COS常见跨域问题汇总
前端开发在使用CDN-COS产品时,经常会遇到应用上的跨域访问,腾讯云COS和CDN两款产品都可以自主设置跨域响应头,但又有一些差异,本文介绍一下常见的访问失败的问题和验证方式。
wainsun
2020/09/14
4.7K0
tengine、nginx配置正向代理,其他内网机器通过代理访问外网,支持https
PCRE(Perl Compatible Regular Expressions)是一个 Perl 库,包括 perl 兼容的正则表达式库。nginx rewrite 依赖于 PCRE 库,所以在安装 Tengine 前一定要先安装 PCRE。
joshua317
2022/03/29
3K0
tengine、nginx配置正向代理,其他内网机器通过代理访问外网,支持https
CDN-COS常见跨域问题汇总
前端开发在使用CDN-COS产品时,经常会遇到应用上的跨域访问,腾讯云COS和CDN两款产品都可以自主设置跨域响应头,但又有一些差异,本文介绍一下常见的访问失败的问题和验证方式。
wainsun
2021/02/23
3K0
CDN-COS常见跨域问题汇总
Nginx 正向代理 http、https(openEuler)
Nginx正向代理,通过服务器代理客户端去重定向请求访问到目标服务器的一种代理服务。对于目标服务器来说浏览器/客户端是隐藏的。Nginx 正向代理默认只支持http 协议,不支持 https 协议,需借助"ngx_http_proxy_connect_module"模块实现https 正向代理。
Kevin song
2024/03/21
3K0
Nginx 正向代理 http、https(openEuler)
nginx--❤️图解及代码实现正向代理、反向代理、负载均衡❤️
在实现nginx正向代理之前,先说明一下,现在的网站基本上都是https,因此要实现nginx正向代理转发请求,除了要配置转发http80端口的请求,还有配置https443端口的请求~
用户4396583
2024/07/25
1.3K0
Nginx专辑|05 -如何使用Nginx配置正向代理
在之前的编译nginx的基础上,我们对nginx二进制增加新的模块ngx_http_proxy_connect_module[1]
公众号: 云原生生态圈
2020/08/04
3K0
Nginx专辑|05 -如何使用Nginx配置正向代理
腾讯云SCF + 腾讯云API网关实现跨域
跨来源资源共享(Cross-Origin Resource Sharing(CORS))是一种使用额外 HTTP 标头来让目前浏览网站的 user agent 能获得访问不同来源(网域)服务器特定资源之权限的机制。当 user agent 请求一个不是目前文件来源——来自于不同网域(domain)、通信协定(protocol)或通信端口(port)的资源时,会建立一个跨来源HTTP请求(cross-origin HTTP request)。
孔令飞
2019/11/01
17.5K1
腾讯云SCF + 腾讯云API网关实现跨域
什么是正向代理以及使用nginx作为正向代理服务器
正向代理(Forward Proxy)是代理服务器的一种使用方式,主要是为了保护客户端,客户端通过代理服务器向目标服务器发起请求,代理服务器接收到请求后将请求转发给目标服务器,并将响应返回给客户端。
玖叁叁
2023/04/11
2.8K0
golang源码分析:http代理和https代理
首先还是上代码:https://github.com/xiazemin/dns_proxy,然后我们思考几个问题:我们使用charles抓包的时候使用的是https代理还是http代理?使用charles代理的时候为什么要装charles的证书,有什么作用?http代理能代理https的请求么?
golangLeetcode
2022/12/17
5440
curl 用法简介
curl 是一个命令行客户端,支持多种传输协议,最经常使用的场景就是在终端请求服务器资源。
后端码匠
2022/01/18
1.9K0
curl 用法简介
nginx正向代理配置
Nginx是一个高性能的Web服务器,也可以作为反向代理和正向代理服务器使用。本文将介绍如何使用Nginx作为正向代理服务器,并提供相关的配置示例。
堕落飞鸟
2023/03/31
2.3K0
正向代理和反向代理
代理按主要服务的角色分成2种,正向代理和反向代理,正向代理代理的是客户端,反向代理代理的是服务器。典型拓扑结构如下:
数据小冰
2022/08/15
5460
正向代理和反向代理
了解HTTP的基本历史及知识
上世纪九十年代前,互联网还没有被发明出来,那时候的网络基本以发邮件(Email1965年发明)等形式简单实用
CRMEB商城源码
2022/04/29
4970
了解HTTP的基本历史及知识
Linux curl支持http/https方法,Curl请求示例语法
Curl请求示例 curl -X GET "http://<host:port>/api/1/test/get?test=<value>&app_id=<app_id>&sign=<sign>&timestamp=<timestamp>" curl -X POST -H "Content-Type: application/json" -d '{"test1":"hah","test2":"qwe","test3":"111"}' "http://<host:port>/api/1/test/post2?test=<value>&app_id=<app_id>&sign=<sign>&timestamp=<timestamp>"
oktokeep
2024/10/09
1660
golang 实现HTTP代理和反向代理
代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端。下图是 《HTTP 权威指南》一书中给出的图例,可以很清晰地说明这一流程:
全栈程序员站长
2022/09/13
2.4K0
golang 实现HTTP代理和反向代理
相关推荐
nginx正向代理配置详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档