由于公司与Alexa平台接入了语音控制的功能,需要将公司的账号与Alexa的账号进行绑定,所以需要账号授权的操作,也就是使用授权码模式。开发过程中遇到了很多坑,网上关于前后端分离的定制页面的介绍又很少,前前后后花了一个月多的时间才搞定。空闲下来准备总结一下,希望可以帮到大家。
关于授权码模式的介绍网上已经有很多的文章了,我这里就不在进行过多的介绍,需要了解的可以看下这两篇文章。
https://blog.csdn.net/qq_41489540/article/details/122813480
oauth2.0授权码模式详解_[虚幻私塾】的博客-CSDN博客_oauth2 授权码
关于前后端分离页面
Oauth2.0虽然自带登录页面,但登录页面太丑,而且对于有网关层面的服务来说,我们一般只会将网关的域名暴漏出去,不会将后端服务的IP暴漏出去,这样用原生的页面的话,也存在不能访问的问题。
比如网关域名是https://xxgateway.com,而auth服务的所在的服务器的IP是192.168.20.2,端口是xxxx。而如果使用Oauth2.0自带的页面,那么当我们使用https://xxgateway.com/xxx/auth/oauth/authorize去获取授权码时,Oauth2.0如果检测到未进行权限认证,会访问http://192.168.20.2:xxxx/login,但一般公网是无法访问192.168.20.2:xxxx的,所以页面都无法加载。
现在一般项目都采用前后端分离,前端使用VUE,后端可能是springboot或者是springCloud.前端项目也需要一个容器来部署,可以使用tomcat或者Nginx。下面用nginx来举例:
- 首先在nginx.conf中添加server,监听某一个端口,比如8089,然后配置location,alias就是前端页面部署的目录,index就是页面,这样访问http://ngnix服务器ip:8089/saber时就会跳到对应的页面上去。到这里前端页面就部署好了。
- 关于登录页面定制,oauth2.0原生的登录页面的action映射是/login,这种映射并不会经过gateway,所以前后端分离的登录页面,需要修改一下action的值,需要让action经过网关转到auth服务去。比如https://xxgateway.com/xxx/auth/login,这样才会使用oauth2.0去校验我们输入的用户名密码。
- 关于定制登录页面的配置,前后端分离的认证,需要将部署的前端页面地址配置到后台去,具体是在WebSecurityConfigurer的类中。其中.loginPage中就需要填写前端页面配置地址http://ngnix服务器ip:8089/saber。
@Override protected void configure(HttpSecurity http) throws Exception { /*http .authorizeRequests() //.antMatchers("/api.auth2/v1/**").authenticated() // 需要验证Token的URL .anyRequest().permitAll();//部分微服务依赖Auth-service的接口,所以都允许访问 http.csrf().disable();*/ http.formLogin().permitAll() .loginPage("") .loginProcessingUrl("/login") .and().logout().permitAll(); http.authorizeRequests() .antMatchers("/oauth/**").authenticated() .anyRequest().permitAll(); http.csrf().disable(); }
4、关于校验成功之后的回调地址问题,当时遇到的问题是alexa配置https://xxgateway.com/xxx/auth/oauth/authorize地址去获取授权码,但其实该地址后面是携带了一些参数的,比如state等,state参数每个请求都是不同的,成功之后需要继续访问这个地址并携带上state参数,这样才能再alexa上进行下一步流程。而oauth2.0认证成功之后的重定向地址也是不会经过网关的,所以回调地址肯定是不对的。当时想了很多办法都不行,后来经和同事商量,决定在gateway进行location重写,因为成功之后的重定向地址是根据location的值来重定向的。我们的网关采用的是zuul, 在网关写了一个filter对location进行重写。其中newUrl的值就是https://xxgateway.com/xxx/auth/oauth/authorize,这样可以替换重定向地址location中的前一部分,而后面的state等参数不变,将地址和参数拼接起来
@Override
public Object run() throws ZuulException {
try {
RequestContext requestContext = RequestContext.getCurrentContext();
int code = requestContext.getResponseStatusCode();
String newUrl = "XXXXX";
StringBuilder builder = new StringBuilder(newUrl);
if (code == 302) {
log.info("ConvertZuulResponseFilter running...");
List<Pair<String, String>> list = requestContext.getZuulResponseHeaders();
for (Pair<String, String> pair : list) {
if (pair.first().equals(GatewayConstants.LOCATION) && pair.second().contains(GatewayConstants.OAUTH_AUTHORIZE)) {
builder.append(pair.second().substring(pair.second().indexOf("?")));
log.info("ConvertZuulResponseFilter newUrl:{}", builder.toString());
pair.setSecond(builder.toString());
}
}
}
}catch (Exception e){
log.error("Convert Zuul Response Headers exception:",e);
}
return null;
}