oauth2授权码模式遇到的坑,1.走网关无法返回授权码 2.refresh_token新token丢失用户信息

2023-11-07

主要有2个坑:

1.通过网关访问/oauth/authorize的时候,无法跳转到redirect_uri返回授权码
2.访问/oauth/token 刷新token的时候,新的token解析后用户信息丢失,用户信息变成了用户名

问题一

通过网关访问/oauth/authorize的时候,会把信息缓存到session中,跳转到登录页登录后,正常来说会从session中取出信息,然后跳转到redirect_uri,但是通过网关访问时,跳到的login登录页是授权服务的,点击登录也是直接访问的授权服务,并没有通过网关,所以需要跳转到login登录页的时候,把ip和端口都替换成网关的,具体代码如下


@TsfGatewayFilter
public class ResponseGlobalFilter extends AbstractTsfGlobalFilter {

    //    @Value("${cors.crossOriginPath}")
    private String crossOriginPath = "网关地址";


    @Override
    public int getOrder() {
        //WRITE_RESPONSE_FILTER 之前执行
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return true;
    }

    @Override
    public Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().value();
        if (path.contains("/oauth/authorize") || path.contains("login")) {
            //构建响应包装类
            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 = "";
            if (StringUtils.isBlank(proxyUrl)) {
                domain = request.getURI().getScheme() + "://" + request.getURI().getAuthority() + "/oauth"; //这是授权服务的服务名
            } else {
                domain = proxyUrl + "/oauth";
            }
            String location = getHeaders().getFirst("Location");
//            for (int i = 0; i < 3; i++) {
//                location = location.substring(location.indexOf("/") + 1);
//            }
            String replaceLocation = location.replaceAll("^((ht|f)tps?):\\/\\/(\\d{1,3}.){3}\\d{1,3}(:\\d+)?", domain);
            if (location.contains("code=")) {
//                getHeaders().set("Location",location );
            } else {
                getHeaders().set("Location", replaceLocation);
            }
        }
        this.getStatusCode();
        return super.writeWith(body);
    }
}

我这是用的腾讯的tsf服务,AbstractTsfGlobalFilter 是腾讯包里的,普通微服务需要写gateway的拦截器,或者zuul的拦截器,gateway就是implements GlobalFilter, Ordered,这样每次访问/oauth/authorize的时候会先走这个过滤器,然后把重定向地址,也就是Location,替换成网关的ip+端口+授权服务名称,这样跳到login页面的时候,就会通过网关跳转,而不是授权服务,就可以获取授权码了。

问题二

在这里插入图片描述
就是这个原因,所以我们需要重写extractAuthentication方法


/**
 * 用户认证转化器
 * /根据 oauth/check_token 的结果转化用户信息
 *
 * @author zhuowen
 * @since 2019/06/21
 */
@Component
public class ZWUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
    private static final String USER_ID = "user_id";
    private static final String DEPT_ID = "dept_id";
    private static final String ROLE_ID = "role_id";
    private static final String NAME = "name";
    private static final String TENANT_ID = "tenant_id";
    private static final String N_A = "N/A";

    /**
     * Extract information about the user to be used in an access token (i.e. for resource servers).
     *
     * @param authentication an authentication representing a user
     * @return a map of key values representing the unique information about the user
     */
    @Override
    public Map<String, ?> convertUserAuthentication(Authentication authentication) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put(USERNAME, authentication.getName());
        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
            response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
        }
        return response;
    }

    /**
     * Inverse of {@link #convertUserAuthentication(Authentication)}. Extracts an Authentication from a map.
     *
     * @param map a map of user information
     * @return an Authentication representing the user or null if there is none
     */
    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        if (map.containsKey(USERNAME)) {
            Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
            String username = (String) map.get(USERNAME);
            String name = (String) map.get(NAME);
            Integer userId = (Integer) map.get("userId");
            ZWUser zwUser = new ZWUser(userId.longValue(),username,name,"N/A",true,true,true,true,authorities);
//            String userStr =  (String)map.get("userInfo");
//            System.out.println("userObj=========="+userStr);
//            ObjectMapper objectMapper = new ObjectMapper();
//            ZWUser user = objectMapper.convertValue(userObj, ZWUser.class);
//            System.out.println("user=========="+user.toString());
//            System.out.println(jsonObject.toJSONString());
//            SysUser user = JSON.parseObject(jsonObject.toJSONString(), SysUser.class);
            return new UsernamePasswordAuthenticationToken(zwUser, N_A, authorities);
        }
        return null;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
        Object authorities = map.get(AUTHORITIES);
        if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                    .collectionToCommaDelimitedString((Collection<?>) authorities));
        }
        throw new IllegalArgumentException("Authorities must be either a String or a Collection");
    }
}

重写完后,我们需要在token增强里配置一下

@Bean
    public JwtAccessTokenConverter accessTokenConverter() {

        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
            /**
             * 重写增强token的方法
             * 自定义返回相应的信息
             *
             */
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                // 与登录时候放进去的UserDetail实现类一直查看link{SecurityConfiguration}
                ZWUser userInfo = (ZWUser) authentication.getUserAuthentication().getPrincipal();
                /* 自定义一些token属性 ***/
                final Map<String, Object> additionalInformation = new HashMap<>(18);
                String name = userInfo.getName();
                Long userId = userInfo.getUser_id();
                additionalInformation.put("name", name);
                additionalInformation.put("userId", userId);
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                return super.enhance(accessToken, authentication);
            }
        };
        accessTokenConverter.setSigningKey(SIGNING_KEY);
        ((DefaultAccessTokenConverter) accessTokenConverter.getAccessTokenConverter()).setUserTokenConverter(zwUserAuthenticationConverter);
        return accessTokenConverter;
    }

最后一行代码accessTokenConverter.getAccessTokenConverter()).setUserTokenConverter(zwUserAuthenticationConverter);把ZWUserAuthenticationConverter 注入到token增强的类里,刷新token后,用户信息就不会丢失了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

oauth2授权码模式遇到的坑,1.走网关无法返回授权码 2.refresh_token新token丢失用户信息 的相关文章

  • java.lang.VerifyError:JVMVRFY012堆栈形状不一致;

    在 WAS 8 5 5 中部署 Maven 项目时出现以下错误 我在WAS中安装了JDK 1 6和1 7 错误500 org springframework web util NestedServletException 处理程序处理失败
  • 从文本文件中读取阿拉伯字符

    我完成了一个项目 在该项目中我读取了用记事本编写的文本文件 我的文本文件中的字符是阿拉伯语 文件编码类型是UTF 8 当在 Netbeans 7 0 1 中启动我的项目时 一切似乎都正常 但是当我将项目构建为 jar 文件时 字符以这种方式
  • 如何作为应用程序发布到页面?

    所以 我有一个应用程序 Facebook 应用程序实体 并且我有一个页面 我想使用应用程序通过java代码 通过restfb或任何其他建议 发布到页面 看起来我错过了页面授予应用程序发布权限的阶段 不知道该怎么做 谢谢你们 乌里 您只能 作
  • 查询 MongoDB 集合中的字段。

    我正在尝试查询 mongodb 集合中的特定字段 这是我的代码和输出 Mongo m new Mongo DB db m getDB mydb DBCollection coll db getCollection student addin
  • 迭代函数可以调用自身吗?

    当观看下面的 MIT 6 001 课程视频时 讲师在 28 00 将此算法标记为迭代 但是 在 30 27 他说这个算法和实际的 递归 算法都是递归的 该函数正在使用基本情况调用自身 那么这次迭代情况如何 private int itera
  • 即使在轴上进行自动量程调整,我也可以保留积分刻度线吗?

    我 偷 了一些代码here http fxexperience com 2012 01 curve fitting and styling areachart 拥有一个AreaChart我在 FXML 中使用了 平滑线条 它的工作原理如下
  • 无法访问“不安全”java方法的java表达式语言

    我正在开发一个项目 让用户向服务器提交小 脚本 然后我将执行这些脚本 有很多脚本语言可以嵌入到Java程序中 例如mvel ognl uel clojure rhino javascript等 但是 据我所知 它们都允许脚本编写者调用Jav
  • 确定序列化对象的类型

    我需要通过套接字发送消息 从用户到引擎的请求 以及从引擎到用户的响应 所以流程本质上是 serialized request Server lt network gt Client serialized response request r
  • 如何在 JPA 和 Hibernate 中将数据库生成的列值定义为只读字段?

    使用 MariaDB 10 2 可以定义日期时间的默认值 例如创建和最后修改 我应该如何将此列作为只读字段访问 因为这个值应该只在数据库的控制之下 并且不应该从代码中修改 但我想在代码中读取这个属性 这很简单 只需设置insertable
  • 插入时的 iBatis 判别器

    我有一个抽象类Example以及与之相伴的具体子类 我使用鉴别器来提取数据out数据库的 像这样
  • 在 java 中运行外部应用程序但不要等待它完成

    我正在用java编写一个应用程序 允许我运行其他应用程序 为此 我使用了 Process 类对象 但当我这样做时 应用程序会等待进程结束 然后再退出 有没有办法在 Java 中运行外部应用程序 但不等待它完成 public static v
  • 如何在不反编译的情况下更改已编译的.class文件?

    我想更改 class 文件方法 我安装 JD Eclipse Decompiler 并打开 class 文件 我添加了一些代码并保存 class 文件 但是 class 文件没有改变 我不知道如何使用反编译器 如果可能的话 如何在不使用反编
  • HTTP 状态 405 - 此 URL java servlet 不支持 HTTP 方法 POST [重复]

    这个问题在这里已经有答案了 我无法使页面正常工作 我有要发布的表单方法和我的 servlet 实现doPost 然而 它不断地向我表明我并不支持POST方法 我只是想做一个简单的网站并将值插入到我的 MySQL 数据库中 type Stat
  • 当底层连接是有状态时如何使用 Apache HttpClient?

    我在谷歌上搜索了很多关于如何使用 HttpClient 进行多线程处理的信息 他们中的大多数人建议使用 ThreadSafeClientConnManager 但我的应用程序必须登录某个主机 登录表单页面 以便 HttpClient 获得底
  • java中使用多线程调用同一类的不同方法

    我有一个类 如下所示 具有三种方法 public class MyRunnable implements Runnable Override public void run what code need to write here to c
  • 如何制作一个makefile只用于编译一些java文件?

    我有三个java文件 名为A java B java C java A将创建对象B B将创建对象C 但我以前从未构建过makefile 有谁可以帮我构建一个 makefile 来编译这三个 java 文件吗 我应该使用什么工具来制作 mak
  • java.lang.IllegalStateException - 提交响应后无法创建会话

    我在我的项目中使用 JSF PrimeFaces 我为此准备了一个Maven项目 当我编译项目并加载主页后 我收到以下异常 java lang IllegalStateException Cannot create a session af
  • 如何使用自定义 JDK 构建 Jenkins 项目?

    我有一个常规的 Jenkins 实例 运行一些多分支管道 该实例在 JDK 11 上运行 因为 Jenkins 并不真正支持更高版本 没关系 但不好的是 我的所有管道似乎也都受到 Java 11 的限制 Jenkins 仅使用它自己也使用的
  • Java/MongoDB 按日期查询

    我将一个值作为 java util Date 存储在我的集合中 但是当我查询以获取两个特定日期之间的值时 我最终得到的值超出了范围 这是我的代码 插入 BasicDBObject object new BasicDBObject objec
  • Java中单例的其他方式[重复]

    这个问题在这里已经有答案了 只是我在考虑编写单例类的其他方法 那么这个类是否被认为是单例类呢 public class MyClass static Myclass myclass static myclass new MyClass pr

随机推荐

  • keil使用指南

    keil使用指南 1 项目的创建 头文件 1 项目的创建 1 首先创建文件夹 2 打开keil软件而后 创建新项目 并定位到1创建的文件夹 3 创建文件 并将文件添加到项目中 4 勾选hex文件生成 5 项目demo编写与编译 编译当前所在
  • 实战wxPython:051- 自定义控件

    前面系列文章中 我们介绍了wxPython提供各种的各种控件 在一般情况下可以满足我们开发应用的各种需求 但是在一些情况下 比如对按钮的美化 一个可以显示CPU占用率的控件等等 这首就需要自定义的控件 才能满足要求 自定义控件可以通过两种方
  • Postgresql 学习记录,模式,分区表,触发器,事务,窗口函数,视图,建表,约束等

    Postgresql 学习记录 模式 分区表 触发器 事务 窗口函数 视图 建表 约束等 PostgreSQL使用一种客户端 服务器的模型 一次PostgreSQL会话由下列相关的进程 程序 组成 一个服务器进程 它管理数据库文件 接受来自
  • 将word文档转换为图片格式的PDF

    0x00 前言 编写一篇文档后 往往会通过转为pdf版本后发布来避免在不同环境下格式出现混乱的情况 但这样操作转出pdf文档可以通过普通的pdf阅读器进行文本的抓取 不能达到我们想要保护知识产权的想法 因此在这里推出一篇如何将word文档转
  • 设计模式之(二)---代理模式Proxy Pattern

    什么是代理模式呢 我很忙 忙的没空理你 那你要找我呢就先找我的代理人吧 那代理人总要知道 被代理人能做哪些事情不能做哪些事情吧 那就是两个人具备同一个接口 代理人虽然不能干活 但是被 代理的人能干活呀 比如西门庆找潘金莲 那潘金莲不好意思答
  • 前端优化-WEB综合

    开启 gzip 压缩 gzip 是 GNUzip 的缩写 最早用于 UNIX 系统的文件压缩 HTTP 协议上的 gzip 编码是一种用来改进 web 应用程序性能的技术 web 服务器和客户端 浏览器 必须共同支持 gzip 目前主流的浏
  • object...args参数

    Java object args参数 Java反射中用到方法public Object newInstance String className Object args 该方法中使用了object args参数 也可以写成object ar
  • 认清现实重新理解游戏的本质

    认清现实重新理解游戏的本质 OVERVIEW 认清现实重新理解游戏的本质 现实 两条小路的启发 四个动机 1 当前的学习任务或工作任务太艰巨 2 完美主义 3 对未来太过于自信 无知 4 大脑小看未来的收益 四个方法 1 让未来的收益足够巨
  • R语言 时间序列ARIMA模型方法

    原理什么的百度一搜一堆 看不明白 先学会用这个工具吧 ARIMA 全称为自回归积分滑动平均模型 Autoregressive Integrated Moving Average Model 简记ARIMA 是由博克思 Box 和詹金斯 Je
  • 使用git提交代码到gerrit

    系统 Linux 需要安装 git 第一步 cd mkdir learn git cd learn git 注意 下面的用户名和邮箱和gerrit网站的账号一定要相同 git config global user name 用户名 git
  • python爬取百度使用kw关键字爬取时出现,百度安全验证,解决方法

    python爬取百度使用kw关键字爬取时出现 百度安全验证 解决方法 之前爬取百度用kw时的代码 没有任何问题 import requests url http www baidu com s headers User Agent Mozi
  • 工具类用于ajax使用json请求时返回一个json

    package com noe utils import java util HashMap 工具类 封装响应内容 会被转成json响应 MyStatus ok gt new MyStatus 0 gt code 0 new MyStatu
  • Jenkins部署及代码静态检查工具Checkstyle集成

    CheckStyle是SourceForge下的一个项目 提供了一个帮助JAVA开发人员遵守某些编码规范的工具 接下来给大家讲下如何在jenkins 我是在tomcat下安装的jenkins 集成checkstyle 构建项目并发送邮件 一
  • Face Alignment / Human Pose Estimation [CVPR‘23, ECCV‘22, NeurIPS‘22]

    CVPR 23 accepted paper list Facial Landmark Face Alignment 论文较少 CVPR 23 3篇 值得关注的子领域 Synthetic Data STAR Loss Reducing Se
  • Service层有多个查询,若依(ruoyi)分页失灵?如何解决呢?

    Service层有多个查询 若依分页失灵 教你如何解决 情景一 当Service层只有一个查询时 若需要分页 只需要在Controller层做以下操作 添加继承BaseController中的startPage 和getDataTable
  • Linux共享内存的查看和删除

    使用的总结 1 共享内存查看 命令 ipcs m Shared Memory Segments key shmid owner perms bytes nattch status 0x00000000 1867776 root 600 39
  • linux ikev1切换到ikev2,IKEv2与IKEv1的差异.doc

    IKEv2与IKEv1的差异 IKEv2与IKEv1的差异IKEv2与IKEv1的差异 摘自RFC4306 附录 A 1 To define the entire IKE protocol in a single document repl
  • 教你动手移植RT-Thread到国产MCU

    摘要 现在芯片价格不断上涨 国内很多厂商也在不断的找替换方案 以ST为例 一个芯片涨了十几倍 蛋疼 最近刚好有机会拿到国产芯片MCU 兆易创新的评估板 GD32350R 板载资源如下 硬件 描述 芯片型号 GD32F350R8T6 CPU
  • AD出Gerber,keepout 不显示(无数据、无外形层)

    AD出Gerber keepout 不显示 无数据 无外形层 版本 16 右键选中keepout 点击 Find Similar Objects 下一步 作如下红框内选择 如图勾选选中板框对象 点击 确定 下一步 先点击变为 True 然后
  • oauth2授权码模式遇到的坑,1.走网关无法返回授权码 2.refresh_token新token丢失用户信息

    主要有2个坑 1 通过网关访问 oauth authorize的时候 无法跳转到redirect uri返回授权码 2 访问 oauth token 刷新token的时候 新的token解析后用户信息丢失 用户信息变成了用户名 问题一 通过