记录一次微服务中使用SpringSecurity集成企业微信

记录一次微服务中使用SpringSecurity集成企业微信扫码登录-校验请求来源错误问题的解决
最近在项目中遇到一个平时不太注意的问题,记录一下方面后期继续深入跟踪,再近期项目中需要使用企业微信扫码登录,在将企业微信二维码嵌入到访问页面中的时候,结果二维码加载失败(校验请求来源错误),结果如下图所示。
失败1.png

场景复现

接入企业微信二维码方式

  1. 企业微信管理端添加访问白名单;
  2. 企业微信管理端设置可信域名(域名要保持和企业微信回调的域名相同)
  3. 必须要通过页面跳转打开访问生成企业微信二维码的链接

实现代码

登录页面(login.html)和跳转链接(忽略掉html布局略丑,单纯为了记录没有做美化)
web1.png

1
2
3
4
html复制代码<a href="https://testapi.xxxx.com/java/troy">企业微信</a>
<p></p>
<a href="https://testapi.xxxx.com/java/troy1">企业微信1</a>
<p></p>

nginx配置

1
2
3
4
5
6
7
8
9
10
11
nginx复制代码 location /java/login {
proxy_pass http://192.168.201.118:9999/java/login;
}

location /java/troy {
proxy_pass http://192.168.201.118:9999/java/troy;
}

location /java/troy1 {
proxy_pass http://192.168.201.118:9999/java/troy1;
}

springboot中springsecurity配置(这里省略掉其他配置)

1
2
3
4
5
6
7
8
java复制代码@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.csrf().disable()
.headers().hsts().disable().frameOptions().mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)
.contentSecurityPolicy("default-src https: data: 'unsafe-inline' 'unsafe-eval'")
.and()//默认ReferrerPolicy是ReferrerPolicy.NO_REFERRER策略
.and()
.cors().configurationSource(corsFilter())

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码@Controller
public class LoginController {

@GetMapping("/java/login")
public String loginPage(Model model) {
return "login";
}
}


@Slf4j
@Controller
public class TestController {

@GetMapping("/java/troy")
public String sso3(ServerWebExchange exchange) {
log.info("come in /troy");
String path = "redirect:https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?appid=xxxxx&redirect_uri=https%3A%2F%2Ftestapi.xxxxx.com%2Fwxwork%2Fsso3-callback&state=073524&usertype=member";
log.info("end /troy");
return path;
}
}

操作流程:

  1. 首先浏览器打开登录地址LoginController(“/java/login”)
  2. 进入登录页面login.html
  3. 点击页面链接进入TestController(“/java/troy”)进行跳转到企业微信API,然后企业微信内部调起生成二维码,页面呈现二维码用户扫码

抛析问题

在使用springsecurity方式失败后,项目组有同学通过go来调用,采用同样的操作流程缺直接打开了企业微信二维码扫码页面,期间也和同事在网上搜了一些二维码加载不出来的问题,但是可用的解决方案没有找到,提到的一些点又太粗,后面通过对比java和go的跳转链接发现,通过跳转后的链接,失败的时候没有http referer属性。既然发现不同点了,那么就好办了看一下springsecurity框架对于referer的处理,再看看能不能加的上,想到这里直接开动起来。

跳转请求对比

我们通过登录页面,执行操作流程中的第3步时,可以通过跳转链接抓到请求查看区别:

1
2
3
4
5
sh复制代码#失败请求
https://aegis.qq.com/collect/pv?id=XfN&uin=&version=1.34.46&aid=xxxxx&platform=4&netType=4&sessionId=session-1198&from=https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?appid=xxxx&redirect_uri=https%3A%2F%2Ftestapi.xxxx.com%2Fwxwork%2Fsso3-callback&state=073524&usertype=member&referer=

#成功请求
https://aegis.qq.com/collect/pv?id=N&uin=&version=1.34.46&aid=xxxx&platform=4&netType=4&sessionId=session-10763&from=https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?appid=xxxx&redirect_uri=https%3A%2F%2Ftestapi.xxxx.com%2Fwxwork%2Fsso3-callback&state=073524&usertype=member&referer=https%3A%2F%2Ftestapi.xxxx.com%2F

通过对比发现失败的请求中缺少referer参数,通过查找springsecurity文档和源码发现,springsecurity会在response header中添加http referer策略,springsecurity默认使用的http referrer策略是ReferrerPolicy.NO_REFERRER。http referrer的介绍可以查看链接。那么到这里问题就简单了修改springsecurity配置,我是用的配置http referrer策略ReferrerPolicy.ORIGIN,大家可以按照自己项目需要选择合理的设置,这里简单讲一下这两个策略的意思,详细解释可以查看链接。

ReferrerPolicy.NO_REFERRER:no-referrer
整个 Referer 首部会被移除。访问来源信息不随着请求一起发送。(这也就解释了为什么失败的时候没有referer的值);

ReferrerPolicy.ORIGIN:origin 不管什么时候只使用origin作为引用地址。例如 www.troyqu.com/page.html的页面只会将www.troyqu.com作为引用地址。

解决问题

修改springboot中springsecurity配置

添加referrerPolicy(ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.ORIGIN)

1
2
3
4
5
6
7
8
java复制代码@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.csrf().disable()
.headers().hsts().disable().frameOptions().mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)
.contentSecurityPolicy("default-src https: data: 'unsafe-inline' 'unsafe-eval'")
.and().referrerPolicy(ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.ORIGIN).and()//默认ReferrerPolicy是ReferrerPolicy.NO_REFERRER策略
.and()
.cors().configurationSource(corsFilter())

重启服务后访问发现,企业微信二维码可以打开了,到这里所有问题都彻底解决了。

成功1.png
那么既然springsecurity有设置http referer那么我们也可以对比下看看是不是更改配置后,对应的参数也进行了更新呢?

默认配置的控制台http参数

可以看到response header中的referer策略已经更新,设置为no-referrer(再次验证)
失败2.png
内部LoginController跳转到TestController接口的时候,request header中无referer参数。
失败3.png
修改后的控制台http参数

可以看到response header中的referer策略已经更新,设置为origin
成功2.png
内部LoginController跳转到TestController接口的时候,request header中referer参数值也和origin一样,没有其他的http path信息。
成功3.png

总结

  1. springsecurity默认使用ReferrerPolicy.NO_REFERRER:no-referrer的策略,当我们需要访问一些其他厂商的API需要验证referrer的时候,会因为请求缺少referrer导致请求被验证不合法,从而导致请求处理失败。
  2. 之前对于http referrer参数没有进行太深入了解,后面需要补一下相关知识。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%