oauth2 通过gateway请求授权码不能回调到return_uri

这个问题拦住我两天,搞了一个周六没搞出来,o(╥﹏╥)o

一、前提

已经使用SpringCloud oauth2 + Security 搭建好了完整的网关 + 认证服务 + 普通的业务的服务
oauth2 通过gateway请求授权码不能回调到return_uri_第1张图片

二、现象

通过网关请求授权码访问认证服务,请求授权码
localhost:8002/api/uaa/oauth/authorize?response_type=code&client_id=c1&redirect_uri=http://www.baidu.com&scope=ROLE_ADMIN

发现跳转到到认证服务的登录页面【注意浏览器的地址栏,ip和端口变成了认证服务的了!!!】

输入账号密码登录后,并没有跳转到到授权页面,而是跳转到认证服务的根目录下
oauth2 通过gateway请求授权码不能回调到return_uri_第2张图片

三、原因

在这里找到原因: https://ask.csdn.net/questions/1061712

总结一下,就是SpringSecurity会缓存登录前的上一次请求在session中,在登录成功后,跳往该请求。由于第一次请求是 localhost:8002/api/uaa/oauth/authorize,到登录时变成了 http://192.168.56.1:8003/login,大家都知道session是根据cookie中的jsessonid来寻找的,由于ip和端口变了,http://192.168.56.1:8003 自然不会携带localhost:8002的cookie,所以登录后的session就不是 localhost:8002/api/uaa/oauth/authorize 当时缓存上一次请求的session

四、解决:

在网关层添加 过滤器,当重定向到认证服务的登录页时,将认证服务的域名和端口替换成网关的域名和端口
oauth2 通过gateway请求授权码不能回调到return_uri_第3张图片

1、网关添加过滤器

@Component
public class ResponseGlobalFilter implements GlobalFilter , Ordered {

    private String crossOriginPath = "http://localhost:8002";


    @Override
    public int getOrder() {
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().value();
        if (path.contains("/oauth/authorize") ) {
            //构建响应包装类
            HttpResponseDecorator responseDecorator = new HttpResponseDecorator(exchange.getRequest(), exchange.getResponse(), crossOriginPath);
            return chain
                    .filter(exchange.mutate().response(responseDecorator).build());
        }
        return chain.filter(exchange);
    }
}


public class HttpResponseDecorator extends ServerHttpResponseDecorator {

    private String proxyUrl;

    private ServerHttpRequest request;

    /**
     * 构造函数
     *
     * @param delegate
     */
    public HttpResponseDecorator(ServerHttpRequest request, ServerHttpResponse delegate, String proxyUrl) {
        super(delegate);
        this.request = request;
        this.proxyUrl = proxyUrl;
    }

    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        HttpStatus status = this.getStatusCode();
        if (status.equals(HttpStatus.FOUND)) {
            String domain = domain = proxyUrl + "/api/uaa";
            String location = getHeaders().getFirst("Location");
            String replaceLocation = location.replaceAll("^((ht|f)tps?):\\/\\/(\\d{1,3}.){3}\\d{1,3}(:\\d+)?", domain);
            if (location.contains("code=")) {

            } else {
                getHeaders().set("Location", replaceLocation);
            }
        }
        this.getStatusCode();
        return super.writeWith(body);
    }
}

2、重启服务后访问授权码

可以看到确实通过网关跳转的登录页
oauth2 通过gateway请求授权码不能回调到return_uri_第4张图片
输入账户密码登录
oauth2 通过gateway请求授权码不能回调到return_uri_第5张图片

1)猜测问题 (自己瞎琢磨的,大家就当看个笑话)

http://localhost:8002/login 这个接口肯定是输入账号密码的认证接口了,SpringSecurity中也是这么配置的,默认表单的提交路径很可能是 ,所以直接就用当前的域名 + 端口 + login (http://localhost:8002/login) 来请求认证了
oauth2 通过gateway请求授权码不能回调到return_uri_第6张图片

3、知道问题后,想办法解决问题

希望将默认表单的 提交路径改为
想了一下,好像没办法改默认表单的提交路径,那么只有重写表单页了

1)uaa服务resource下,新建statc文件夹,新建MyLogin.html 表单登录页

oauth2 通过gateway请求授权码不能回调到return_uri_第7张图片

2) MyLogin.html 表达提交路径改为 /api/uaa/login

DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录title>
head>
<body>
<div class="login-container">
    <div class="form-container">
        <form class="form-signin" method="post" action="/api/uaa/login">
            <h2 class="form-signin-heading">用户登录h2>
            <p>
                <label for="username" class="sr-only">用户名label>
                <input type="text" id="username" name="username" class="form-control" placeholder="用户名" required
                       autofocus>
            p>
            <p>
                <label for="password" class="sr-only">密码label>
                <input type="password" id="password" name="password" class="form-control" placeholder="密码" required>
            p>
            <button class="btn btn-lg btn-primary btn-block" type="submit">  button>
        form>
    div>
div>
body>
<style>
    .login-container {
        margin: 50px;
        width: 100%;
    }

    .form-container {
        margin: 0px auto;
        width: 50%;
        text-align: center;
        box-shadow: 1px 1px 10px #888888;
        height: 300px;
        padding: 5px;
    }

    input {
        margin-top: 10px;
        width: 350px;
        height: 30px;
        border-radius: 3px;
        border: 1px #E9686B solid;
        padding-left: 2px;

    }


    .btn {
        width: 350px;
        height: 35px;
        line-height: 35px;
        cursor: pointer;
        margin-top: 20px;
        border-radius: 3px;
        background-color: #E9686B;
        color: white;
        border: none;
        font-size: 15px;
    }

    .title {
        margin-top: 5px;
        font-size: 18px;
        color: #E9686B;
    }
style>
html>

3)及得SpringSecurity的配置 WebSecurityConfig 配置 loginPage和 loginProcessingUrl

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().formLogin()
                .loginPage("MyLogin.html")
                .loginProcessingUrl("/login").permitAll()
       //       .successHandler(successHandler).permitAll()
                .failureHandler(failureHandler).permitAll().and()
                .logout().logoutSuccessHandler(logoutHandler).and()
                .authorizeRequests()
                .antMatchers("/security/**").permitAll();
    }

4) 测试

确实跳转到登录页
oauth2 通过gateway请求授权码不能回调到return_uri_第8张图片
点击登录后,跳转到授权页面
oauth2 通过gateway请求授权码不能回调到return_uri_第9张图片

点击授权后,看来和上面是一个问题,需要将默认授权页面的提交接口从 /oauth/authorize 改成
/api/uaa/oauth/authorize
oauth2 通过gateway请求授权码不能回调到return_uri_第10张图片

5) 自定义授权页面

a、uaa引入 thymeleaf

 
		<dependency>
		    <groupId>org.springframework.bootgroupId>
		    <artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>

b、配置文件添加thymeleaf配置

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html

c、在resources目录下面建立templates目录,新建 grant.html
【注意】form提交路径要改为 /api/uaa/oauth/authorize

DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>授权title>
head>
<body style="margin: 0px">
<div class="title">
    <div class="title-right">oauth2授权div>
    <div class="title-left">
        <a href="#help">帮助a>
    div>
div>
<div class="container">
    <h3 th:text="${clientId}+' 请求授权,该应用将获取你的以下信息'">h3>
    <p>昵称,头像和性别p>
    授权后表明你已同意 <a  href="#boot" style="color: #E9686B">服务协议a>
    <form method="post" action="/api/uaa/oauth/authorize">
        <input type="hidden" name="user_oauth_approval" value="true">
        <div th:each="item:${scopes}">
            <input type="radio" th:name="'scope.'+${item}" value="true" checked>同意
            <input type="radio" th:name="'scope.'+${item}" value="false" >拒绝
        div>
        <input name="authorize" value="同意/授权" type="submit">
    form>

div>
<style>

    html{
        padding: 0px;
        margin: 0px;
    }

    .title {
        background-color: #E9686B;
        height: 50px;
        padding-left: 20%;
        padding-right: 20%;
        color: white;
        line-height: 50px;
        font-size: 18px;
    }
    .title-left{
        float: right;
    }
    .title-right{
        float: left;
    }
    .title-left a{
        color: white;
    }
    .container{
        clear: both;
        text-align: center;
    }
    .btn {
        width: 350px;
        height: 35px;
        line-height: 35px;
        cursor: pointer;
        margin-top: 20px;
        border-radius: 3px;
        background-color: #E9686B;
        color: white;
        border: none;
        font-size: 15px;
    }
style>
body>
html>

d、新写controller覆盖oauth2 的 confirm_access接口

package com.uaa.controller;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

@Controller
//必须配置
@SessionAttributes("authorizationRequest")
public class BootGrantController {

 @RequestMapping("/oauth/confirm_access")
 public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
     AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
     ModelAndView view = new ModelAndView();
     view.setViewName("grant"); //自定义页面名字,resources\templates\base-grant.html
     view.addObject("clientId", authorizationRequest.getClientId());
     view.addObject("scopes",authorizationRequest.getScope());
     return view;
 }
}

e、测试
oauth2 通过gateway请求授权码不能回调到return_uri_第11张图片
点击同意后,顺利拿到回调的code
在这里插入图片描述

五、总结

虽然解决了问题,但是感觉自己写的东西太多了,待会再研究下有没有更好的解决方案吧!!!
不开心 o(╥﹏╥)o

你可能感兴趣的:(OAuth2,oauth2,授权码)