使用新浪API自动登录报异常:com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden

部署运行你感兴趣的模型镜像

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) 

http://blog.youkuaiyun.com/floodingfire/article/details/8142974


自动登录代码

Weibo weibo = Weibo.getInstance();
AccessToken accessToken = new AccessToken(accessToken, SINA_API_SECRET);
accessToken.setExpiresIn("99999");
weibo.setAccessToken(accessToken);

在使用新浪开放平台的OAuth2.0授权后自动登录时,一直在报同一个异常:

com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden
这个问题困扰我近月,上网搜寻答案也不得解脱。最后还是在新浪SDK源码中发现蛛丝马迹终于解决了这个问题。

Step1:在LogCat下查看错误信息

11:00:03.129: W/System.err(557): com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden  (1. 未授权)
11:00:03.149: W/System.err(557): 	at com.weibo.net.Utility.openUrl(Utility.java:335) (2. 错误发生在这里)
11:00:03.149: W/System.err(557): 	at com.weibo.net.Utility.openUrl(Utility.java:286)
11:00:03.178: W/System.err(557): 	at com.weibo.net.Weibo.request(Weibo.java:149)
11:00:03.178: W/System.err(557): 	at com.weibo.net.AsyncWeiboRunner$1.run(AsyncWeiboRunner.java:51)
11:00:03.218: W/share(557): WeiboException: com.weibo.net.WeiboException: HTTP/1.1 403 Forbidden
Step2:分析原因

首先错误消息是403状态码,有点儿常识的应该知道404代表没有访问资源、找不到资源等,而403大致是没有访问权、禁止访问等。(详细的解释Google之)

其实在新浪的文档和SDK的源码中都有关于异常的解释,比如在新浪SDK下,有这么一条注释:

com/weibo/net/WeiboException.java

403 Forbidden: 没有权限访问对应的资源.

可是明明传入了accessToken,怎么会没有访问权限呢?

不要急躁,顺着LogCat的信息顺藤摸瓜:StackTrace的栈顶信息显示错误最后出现在Utility.java中的openUrl()方法中:throw new WeiboException(String.format(status.toString()), statusCode);

public static String openUrl(Context context, String url, String method,
		WeiboParameters params, String file, Token token) throws WeiboException {
	String result = "";
	try {
		HttpClient client = getNewHttpClient(context);
		HttpUriRequest request = null;
		ByteArrayOutputStream bos = null;
		if (method.equals("GET")) {
			url = url + "?" + encodeUrl(params);
			HttpGet get = new HttpGet(url);
			request = get;
		} else if (method.equals("POST")) {
			HttpPost post = new HttpPost(url);
			byte[] data = null;
			bos = new ByteArrayOutputStream(1024 * 50);
			if (!TextUtils.isEmpty(file)) {
				Utility.paramToUpload(bos, params);
				post.setHeader("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);
				Bitmap bf = BitmapFactory.decodeFile(file);

				Utility.imageContentToUpload(bos, bf);

			} else {
				post.setHeader("Content-Type", "application/x-www-form-urlencoded");
				String postParam = encodeParameters(params);
				data = postParam.getBytes("UTF-8");
				bos.write(data);
			}
			data = bos.toByteArray();
			bos.close();
			// UrlEncodedFormEntity entity = getPostParamters(params);
			ByteArrayEntity formEntity = new ByteArrayEntity(data);
			post.setEntity(formEntity);
			request = post;
		} else if (method.equals("DELETE")) {
			request = new HttpDelete(url);
		}
		setHeader(method, request, params, url, token);
		HttpResponse response = client.execute(request);
		StatusLine status = response.getStatusLine();
		int statusCode = status.getStatusCode();

		if (statusCode != 200) {
			result = read(response);
			throw new WeiboException(String.format(status.toString()), statusCode);
		}
		// parse content stream from response
		result = read(response);
		return result;
	} catch (IOException e) {
		throw new WeiboException(e);
	}
}

可以看到请求完毕以后,判断statusCode不为200(请求正常)状态码。也就是错误出现在发送请求之前,继续往上看,这个方法用的是Android中常用的一种网络通信方法:

HttpResponse response = client.execute(request);
那么在HttpClient执行此次请求之前,又发生了什么事情呢?

setHeader(method, request, params, url, token);
看方法的名称似乎像是设置HTTP的头部信息,ok,再跟进代码看看(其实有经验的人看到这个方法的参数,就已经明白是怎么回事了):
// 设置http头,如果authParam不为空,则表示当前有token认证信息需要加入到头中
public static void setHeader(String httpMethod, HttpUriRequest request,
		WeiboParameters authParam, String url, Token token) throws WeiboException {
	if (!isBundleEmpty(mRequestHeader)) {
		for (int loc = 0; loc < mRequestHeader.size(); loc++) {
			String key = mRequestHeader.getKey(loc);
			request.setHeader(key, mRequestHeader.getValue(key));
		}
	}
	if (!isBundleEmpty(authParam) && mAuth != null) {
		String authHeader = mAuth.getWeiboAuthHeader(httpMethod, url, authParam,
				Weibo.getAppKey(), Weibo.getAppSecret(), token);
		if (authHeader != null) {
			request.setHeader("Authorization", authHeader);
		}
	}
	request.setHeader("User-Agent", System.getProperties().getProperty("http.agent")
			+ " WeiboAndroidSDK");
}
这个方法的注释已经说明了问题,再仔细查看代码,问题就出在mAuth变量中,显然这个变量不是从刚才的方法传递过来的,那么这个方法怎么又要依赖一个莫名其妙的mAuth变量呢?

private static HttpHeaderFactory mAuth;
而查看整个Utility类(如何查看类中变量被使用的位置不必多说吧?),mAuth出现的次数并不多,除了在setHeader方法中有惊鸿一瞥,它的真正老家再另一个方法中:
public static void setAuthorization(HttpHeaderFactory auth) {
	mAuth = auth;
}
看到这里我想大家除了想骂娘,没有别的心思了。

国内的大平台如新浪、腾讯、人人一贯如此,强内聚、弱耦合似乎只是个说法而已,虽然说一个大项目要做好扩展,但是好歹也要给个提示嘛!!!

Step3:解决问题

好了,小小的抱怨一下,我们的任务还没有完成,大致思路明确,要在此方法前初始化mAuth参数
可是,参数是HttpHeaderFactory 对象啊,跟代码过去一看,又傻了:

public abstract class HttpHeaderFactory {
    public static final String CONST_HMAC_SHA1 = "HmacSHA1";
    public static final String CONST_SIGNATURE_METHOD = "HMAC-SHA1";
    public static final String CONST_OAUTH_VERSION = "1.0";

    public HttpHeaderFactory() {
    }
...
这,尼玛!竟然是抽象类?本来憋着一股气,这又要怒气冲天了。

HOLD住,冲动易怒可不是好程序员的特质!既然是用的抽象类,那么前面的错误也明了,写这个代码的人可能是为了扩展性,既然是这样,他可能写好了实现类。如果连实现类都没有,大家就尽情的愤怒,然后安静的自己实现一个吧。

方案一:自己实现

....我这么懒,是不会做这种傻事的。


方案二:查找实现类

怎么个查找法?把所有的类都看一遍,看看是不是 extends HttpHeaderFactory? 是这么想的童鞋,先去面壁5分钟。

...

ok,有经验的程序员应该知道,在Eclipse中查看继承关系是有快捷方式的,选中类以后按:Ctrl + T。

这一下又要犯愁了,一下子揪出了他一家子,可见这个程序员确实是为了扩展,但是这么多的子类,到底用哪一个好呢?

你可以一个个看看,但是我用的是OAuth2.0,直接用试试Oauth2AccessTokenHeader好了。看着注释里那个半吊子英语,我又笑了~~should not be...

/**
 * Encapsulation a http accessToken headers. the order of weiboParameters will not be changed.
 * Otherwise the signature should not be calculated right.
 * @author  ZhangJie (zhangjie2@staff.sina.com.cn)
 */
public class Oauth2AccessTokenHeader extends HttpHeaderFactory {

    @Override
    public String getWeiboAuthHeader(String method, String url, WeiboParameters params,
            String app_key, String app_secret, Token token) throws WeiboException {
        if(token == null){
            return null;
        }
        return "OAuth2 " + token.getToken();
        
    }
	@Override
	public WeiboParameters generateSignatureList(WeiboParameters bundle) {
	    return null;
	}

	@Override
	public String generateSignature(String data, Token token) throws WeiboException{
		return "";
	}

	@Override
	public void addAdditionalParams(WeiboParameters des, WeiboParameters src) {
		// TODO Auto-generated method stub
		
	}

}
ok,直接使用无参构造方法初始化,作为参数,在使用相应API之前初始化mAuth参数,只有mAuth参数不为null时,Utility.setHeader才会拼凑出正确的HTTP Header来,否则header为空,token信息没有携带过去,就导致未授权错误,返回码是403了。
Utility.setAuthorization(new Oauth2AccessTokenHeader());


附录

正确的自动登录方法为:

Weibo weibo = Weibo.getInstance();
Utility.setAuthorization(new Oauth2AccessTokenHeader());
AccessToken accessToken = new AccessToken(accessToken, SINA_API_SECRET);
accessToken.setExpiresIn("99999");
weibo.setAccessToken(accessToken);


您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

<think>我们正在处理用户的问题:POST请求 /tool/mysql 出现 403 Forbidden 错误的原因及解决办法。 根据用户提供的引用内容,我们可以得到一些线索: 引用[1]提到:豆瓣图片资源访问时出现403错误是因为Referer策略,即服务器只允许特定来源的请求访问资源。 引用[2]提到:在搭建ELK日志平台时,由于误用root账号启动导致文件权限问题。 但用户的问题是关于POST请求/tool/mysql返回403 Forbidden错误。403错误通常表示服务器理解请求,但拒绝执行。可能的原因包括: 1. 身份验证问题:请求未提供有效的身份凭证,或者提供的凭证权限不足。 2. CSRF(跨站请求伪造)保护:在Web应用中,如果启用了CSRF保护,而请求中没有携带有效的CSRF token,则可能被拒绝。 3. IP地址限制:服务器可能配置了IP白名单,只允许特定IP访问。 4. 请求头问题:比如缺少必要的请求头,或者请求头不符合服务器要求(如Referer、User-Agent等)。 5. 文件或目录权限问题:如果请求涉及服务器文件系统,可能是服务器进程对相关目录或文件没有足够的权限(类似于引用[2]中的权限问题)。 6. 应用层权限控制:应用程序自身的权限控制逻辑拒绝了该请求。 结合用户的具体错误信息:'INFO: 172.20.10.12:56690 - "POST /tool/mysql HTTP/1.1" 403 Forbidden' 我们可以从以下几个方面分析: 可能原因分析: 1. 缺少身份验证:检查请求是否包含了正确的认证信息(如Token、Session Cookie等)。 2. CSRF Token缺失:如果应用使用了CSRF保护(例如在表单提交中),POST请求需要携带CSRF Token,否则会被拒绝。 3. Referer策略:类似于引用[1]的情况,服务器可能检查Referer头,如果请求来自未经授权的域名,则拒绝。但注意,用户错误是发生在/tool/mysql,可能是一个API接口,不一定有Referer限制,但也不能排除。 4. IP限制:服务器可能只允许特定IP访问/tool/mysql接口,而172.20.10.12不在白名单中。 5. 权限配置错误:服务器上运行的应用可能没有足够的权限访问相关资源(比如数据库连接权限、文件系统权限等)。 6. 应用逻辑拒绝:可能由于业务逻辑原因(如用户没有操作权限)而拒绝。 解决方案建议: 1. 检查身份验证:确保请求携带了正确的认证信息(如JWT、Cookie等)。如果是API,检查API密钥是否正确。 2. 检查CSRF保护:如果应用有CSRF保护,确保POST请求中包含了有效的CSRF token。注意,如果使用RESTful API,可能没有CSRF保护,但如果是通过浏览器发起的请求(比如表单提交),则很可能需要。 3. 检查Referer头:如果是通过浏览器发起的请求,查看请求的Referer头是否被服务器接受。可以在开发者工具的Network中查看请求头。如果确实是因为Referer,可以尝试在服务器端调整Referer策略(如果可控),或者在请求头中添加Referer(但需要确保是合法的,且不能绕过安全机制)。 4. 检查IP白名单:确认服务器是否配置了IP白名单,并将客户端IP(172.20.10.12)加入白名单。 5. 检查服务器权限:确保运行应用程序的用户具有执行该操作所需的权限(如数据库访问权限、文件读写权限等)。类似于引用[2]中的权限问题,检查相关文件和目录的权限。 6. 查看应用日志:服务器端应用日志通常会记录更详细的拒绝原因,查看日志可以定位问题。 7. 使用工具调试:使用Postman或curl模拟请求,逐步添加必要的头信息或参数,以确定是哪种原因。 由于用户的问题中提到了具体的IP和端口(172.20.10.12:56690),这可能是客户端的地址,而错误发生在服务器端。因此,重点应放在服务器端的配置和应用程序的权限控制上。 总结步骤: 1. 验证请求是否包含必要的认证信息。 2. 检查CSRF token(如果适用)。 3. 检查Referer头(如果适用)。 4. 检查服务器IP白名单设置。 5. 检查服务器端应用程序的权限配置(包括文件系统权限和数据库权限)。 6. 查看服务器端应用程序的日志,获取更详细的错误信息。 如果用户能够提供更多上下文(如服务器环境、应用程序框架、请求的具体方式等),可以更精确地定位问题。 根据上述分析,我们生成相关问题,以便用户进一步排查。 </think> ### POST请求 `/tool/mysql` 出现 `403 Forbidden` 错误的原因及解决方案 #### 错误原因分析 1. **身份验证问题** - 请求未携带有效凭证(如API密钥、Token或Cookie) - 用户权限不足(例如账户无操作MySQL的权限) - 凭证已过期或失效 2. **安全策略拦截** - **Referer策略限制**:类似引用[1]中的豆瓣案例,服务器可能要求特定来源(如域名)才能访问接口[^1] - **CSRF保护**:框架(如Django/Flask)的跨站请求伪造防护拦截了未携带有效Token的请求 - **IP黑白名单**:客户端IP `172.20.10.12` 被服务器拒绝访问 3. **权限配置错误** - 应用进程权限不足(如引用[2]的权限问题,进程需访问数据库但无权限)[^2] - 服务器防火墙/安全组规则拦截 - URL路径 `/tool/mysql` 被Web服务器(Nginx/Apache)配置为禁止访问 #### 解决方案 1. **基础检查** ```bash # 检查网络连通性 curl -X POST http://your-server/tool/mysql -v ``` - 观察响应头中的 `WWW-Authenticate` 字段确认认证方式 - 验证客户端IP是否在服务端白名单中 2. **身份验证修复** - 添加有效凭证到请求头: ```python # Python requests示例 headers = {"Authorization": "Bearer YOUR_TOKEN"} requests.post("/tool/mysql", headers=headers) ``` - 更新过期密钥或重新登录获取新会话 3. **安全策略调整** - **Referer问题**:在请求头添加合法来源(需服务端允许): ```http Referer: https://your-allowed-domain.com ``` - **CSRF保护**: - 前端表单添加 `{% csrf_token %}`(Django) - 请求头添加 `X-CSRFToken`(从Cookie获取) 4. **服务端排查** - 检查Web服务器配置: ```nginx # Nginx示例:解除路径限制 location /tool/mysql { allow 172.20.10.12; # 添加客户端IP proxy_pass http://backend; } ``` - 验证应用进程权限(参考引用[2]): ```bash # 检查文件权限(如配置文件/数据库凭证) ls -l /path/to/mysql_config.cnf chown appuser:appgroup /path/to/keyfile # 修正属主 ``` 5. **日志分析** - 查看服务端错误日志定位具体拒绝原因: ```bash tail -f /var/log/nginx/error.log # Nginx journalctl -u your-app.service # Systemd服务 ``` #### 预防措施 - 使用Postman测试接口,逐步添加请求头定位问题源 - 服务端启用详细日志记录访问拒绝原因 - 遵循最小权限原则配置应用账户和访问策略 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值