Spring Security Oauth2 之 理解OAuth 2.0授权流程
1. Oauth 定义
1.1 角色
OAuth定义了四个角色:
资源所有者
一个能够授权访问受保护资源的实体。当资源所有者是一个人时,它被称为最终用户。
资源服务器
托管受保护资源的服务器能够使用访问令牌接受和响应受保护的资源请求。
客户
代表资源所有者及其授权的应用程序进行受保护的资源请求。术语客户端并不意味着任何特定的实现特征(例如,应用程序是在服务器,台式机还是其他设备上执行的)。
授权服务器
服务器在成功认证资源所有者并获得授权后向客户端发放访问令牌。
1.2 协议流程
图1中所示的抽象OAuth 2.0流程描述了四个角色之间的交互,并包含以下步骤:
(A) 客户端请求资源所有者的授权。授权请求可以直接给资源所有者(如图所示),或者优选间接地通过授权服务器作为中介。
(B) 客户端接收授权许可,这是一种代表资源所有者授权的凭证,使用本规范中定义的四种授权类型之一或使用扩展授权类型表示。授权授予类型取决于客户端用于请求授权的方法以及授权服务器支持的类型。
(C) 客户端通过向授权服务器进行认证并携带授权来请求访问令牌。
(D) 授权服务器对客户端进行身份验证并验证授权,并且如果有效则发出访问令牌。
(E) 客户端从资源服务器请求受保护的资源并通过携带访问令牌进行认证。
(F) 资源服务器验证访问令牌,并且如果有效,则为该请求提供服务。
客户从资源所有者(步骤(A)和(B)中描述)获得授权许可的首选方法是使用授权服务器作为中介
2 模式
oauth2根据使用场景不同,分成了4种模式
● 客户端模式(client credentials):标准的 Server 授权模式,非常适合 Server 端的 Web 应用。一旦资源的拥有者授权访问他们的数据之后,他们将会被重定向到 Web 应用并在 URL 的查询参数中附带一个授权码(code)。在客户端里,该 code 用于请求访问令牌(access_token)。并且该令牌交换的过程是两个服务端之前完成的,防止其他人甚至是资源拥有者本人得到该令牌。另外,在该授权模式下可以通过 refresh_token 来刷新令牌以延长访问授权时间,也是最为复杂的一种方式。
● 密码模式(resource owner password credentials) : 自己有一套用户体系,这种模式要求用户提供用户名和密码来交换访问令牌(access_token)。该模式仅用于非常值得信任的用户,例如API提供者本人所写的移动应用。虽然用户也要求提供密码,但并不需要存储在设备上。因为初始验证之后,只需将 OAuth 的令牌记录下来即可。如果用户希望取消授权,因为其真实密码并没有被记录,因此无需修改密码就可以立即取消授权。token本身也只是得到有限的授权,因此相比最传统的 username/password 授权,该模式依然更为安全。
● 授权码模式(authorization code) :该模式是所有授权模式中最简单的一种,并为运行于浏览器中的脚本应用做了优化。当用户访问该应用时,服务端会立即生成一个新的访问令牌(access_token)并通过URL的#hash段传回客户端。这时,客户端就可以利用JavaScript等将其取出然后请求API接口。该模式不需要授权码(code),当然也不会提供refresh token以获得长期访问的入口。
● 简化模式(implicit) :没有用户的概念,一种基于 APP 的密钥直接进行授权,因此 APP 的权限非常大。它适合像数据库或存储服务器这种对 API 的访问需求。
Oauth基于客户端与认证服务器验证的能力定义了两种客户端类型(以及,维护客户端认证信息的能力): 客户端模式、密码模式。
基础参数定义:
grant_type (发放令牌类型)、
client_id (客户端标识id)
username(用户帐号)
password (用户密码)
client_secret(客户端标识密钥)
refresh_token (刷新令牌)
scope(表示权限范围,可选项)
Oauth2 Client 集成
pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
package com.lvyuanj.upms.oauthclient.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;@Configuration
public class UpmsOauth2LoginConfig {@EnableWebSecuritypublic static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UpmsLogoutHandler upmsLogoutHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().logout().addLogoutHandler(upmsLogoutHandler).and().oauth2Login();}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().mvcMatchers("/js/**","/css/**");}}@Beanpublic OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);}@Beanpublic OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);}@Beanpublic ClientRegistrationRepository clientRegistrationRepository() {return new InMemoryClientRegistrationRepository(this.upmsOauth2ClientRegistration());}@Bean@ConfigurationProperties(prefix = "spring.security.oauth2.client.registration.upms")public UpmsClientRegistration upmsClientRegistration(){return new UpmsClientRegistration();}@Bean@ConfigurationProperties(prefix = "spring.security.oauth2.client.provider.upms")public UpmsClientProvider upmsClientProvider(){return new UpmsClientProvider();}private ClientRegistration upmsOauth2ClientRegistration() {return ClientRegistration.withRegistrationId(this.upmsClientRegistration().getProvider()).clientId(this.upmsClientRegistration().getClientId()).clientSecret(this.upmsClientRegistration().getClientSecret()).clientAuthenticationMethod(ClientAuthenticationMethod.BASIC).authorizationGrantType(new AuthorizationGrantType(this.upmsClientRegistration().getAuthorizationGrantType())).redirectUriTemplate(this.upmsClientRegistration().getRedirectUriTemplate()).scope(this.upmsClientRegistration().getScope()).authorizationUri(this.upmsClientProvider().getAuthorizationUri()).tokenUri(this.upmsClientProvider().getTokenUri()).userInfoUri(this.upmsClientProvider().getUserInfoUri()).userNameAttributeName(this.upmsClientProvider().getUserNameAttribute()).clientName(this.upmsClientRegistration().getClientName()).build();}
}
oauth registration config
package com.lvyuanj.upms.oauthclient.config;import lombok.Data;@Data
public class UpmsClientProvider {private String authorizationUri;private String tokenUri;private String userInfoUri;private String userNameAttribute;
}package com.lvyuanj.upms.oauthclient.config;import lombok.Data;@Data
public class UpmsClientRegistration {private String provider;private String clientId;private String clientSecret;private String clientName;private String authorizationGrantType;private String redirectUriTemplate;private String scope;}
退出登陆配置
package com.lvyuanj.upms.oauthclient.config;import com.lvyuanj.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Slf4j
@Service
public class UpmsLogoutHandler implements LogoutHandler {@Value("${com.lvyuanj.upms.client.upms.logout-uri}")private String logoutUrl;@Overridepublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {try {String access_token = (String) request.getSession().getAttribute("access_token");String params = "";if(StringUtils.isNotBlank(access_token)){params = "?access_token="+access_token;}response.sendRedirect(logoutUrl+params);} catch (IOException e) {e.printStackTrace();}}
}
登陆成功跳转到index
package com.lvyuanj.upms.oauthclient.controller;import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Map;@Slf4j
@Controller
public class OAuth2Controller {@Autowiredprivate OAuth2AuthorizedClientService authorizedClientService;@RequestMapping("/")public String index(Model model, OAuth2AuthenticationToken authentication, HttpServletRequest request) {log.debug("authentication:"+ JSONObject.toJSONString(authentication));OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);if(null != authorizedClient){String access_token = authorizedClient.getAccessToken().getTokenValue();log.debug("access_token: "+ access_token);request.getSession().setAttribute("access_token", access_token);model.addAttribute("userName", authentication.getName());model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName());return "index";}else {return "logout";}}private OAuth2AuthorizedClient getAuthorizedClient(OAuth2AuthenticationToken authentication) {return this.authorizedClientService.loadAuthorizedClient(authentication.getAuthorizedClientRegistrationId(), authentication.getName());}@RequestMapping("/userinfo")public String userinfo(Model model,OAuth2AuthenticationToken authentication) {// authentication.getAuthorizedClientRegistrationId() returns the// registrationId of the Client that was authorized during the Login flowOAuth2AuthorizedClient authorizedClient =this.authorizedClientService.loadAuthorizedClient(authentication.getAuthorizedClientRegistrationId(),authentication.getName());OAuth2AccessToken accessToken = authorizedClient.getAccessToken();System.out.println(accessToken.getTokenValue());Map userAttributes = Collections.emptyMap();String userInfoEndpointUri = authorizedClient.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();if (!StringUtils.isEmpty(userInfoEndpointUri)) {// userInfoEndpointUri is optional for OIDC ClientsuserAttributes = WebClient.builder().filter(oauth2Credentials(authorizedClient)).build().get().uri(userInfoEndpointUri).retrieve().bodyToMono(Map.class).block();}model.addAttribute("userAttributes", userAttributes);return "userinfo";}private ExchangeFilterFunction oauth2Credentials(OAuth2AuthorizedClient authorizedClient) {return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {ClientRequest authorizedRequest = ClientRequest.from(clientRequest).header(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue()).build();return Mono.just(authorizedRequest);});}
}
Spring Security Oauth2 Client 非常重要的过滤器:OAuth2LoginAuthenticationFilter
org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter#attemptAuthentication方法
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*"; --在oauth2登陆成功之后,此过滤器拦截回调接口地址@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException, IOException, ServletException {MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) { // 判断回调数据是否有CODE 或者 STATE 参数OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());}OAuth2AuthorizationRequest authorizationRequest =this.authorizationRequestRepository.removeAuthorizationRequest(request, response);if (authorizationRequest == null) {OAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE);throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());}String registrationId = (String) authorizationRequest.getAdditionalParameters().get(OAuth2ParameterNames.REGISTRATION_ID);ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);if (clientRegistration == null) {OAuth2Error oauth2Error = new OAuth2Error(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE,"Client Registration not found with Id: " + registrationId, null);throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());}String redirectUri = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)).replaceQuery(null).build().toUriString();OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri);OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));OAuth2LoginAuthenticationToken authenticationResult =(OAuth2LoginAuthenticationToken) this.getAuthenticationManager().authenticate(authenticationRequest);OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(authenticationResult.getPrincipal(),authenticationResult.getAuthorities(),authenticationResult.getClientRegistration().getRegistrationId());OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(authenticationResult.getClientRegistration(),oauth2Authentication.getName(),authenticationResult.getAccessToken(),authenticationResult.getRefreshToken());this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);return oauth2Authentication;}
相关文章:
Spring Security Oauth2 之 理解OAuth 2.0授权流程
1. Oauth 定义 1.1 角色 OAuth定义了四个角色: 资源所有者 一个能够授权访问受保护资源的实体。当资源所有者是一个人时,它被称为最终用户。 资源服务器 托管受保护资源的服务器能够使用访问令牌接受和响应受保护的资源请求。 客户 代表资源所有…...
mysql题目4
tj11: select count(*) 员工总人数 from tb_dept a join tb_employee b on a.deptnob.deptno where a.dname 市场部...
GFS部署实验
目录 1、部署环境 编辑 2、更改节点名称 3、准备环境 4、磁盘分区,并挂载 5. 做主机映射--/etc/hosts/ 6. 复制脚本文件 7. 执行脚本完成分区 8. 安装客户端软件 1. 安装解压源包 2. 创建gfs 3. 安装 gfs 4. 开启服务 9、 添加节点到存储信任池中 1…...
最前沿・量子退火建模方法(1) : subQUBO讲解和python实现
前言 量子退火机在小规模问题上的效果得到了有效验证,但是由于物理量子比特的大规模制备以及噪声的影响,还没有办法再大规模的场景下应用。 这时候就需要我们思考,如何通过软件的方法怎么样把大的问题分解成小的问题,以便通过现在…...
如何在Linux部署MeterSphere并实现公网访问进行远程测试工作
文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…...
postgis导入shp数据时“dbf file (.dbf) can not be opened.“
作者进行矢量数据导入数据库中出现上述报错 导致报错原因 导入的shp文件路径太深导入的shp文件名称或路径中有中文将需要导入数据的shp 文件、dbf 文件、prj 等文件放在到同一个文件夹内,且名字要一致;导入失败: 导入成功:...
StarUML笔记之从C++代码生成UML图
StarUML笔记之从C代码生成UML图 —— 2024-04-14 文章目录 StarUML笔记之从C代码生成UML图1.安装C插件2.准备好一个C代码文件放某个路径下3.点击Reverse Code选择项目文件夹4.拖动(Class)到中间画面可以形成UML5.另外一种方式:双击Type Hierarchy,然后…...
sizeof()和strlen
一、什么是sizeof() sizeof()是一个在C和C中广泛使用的操作符,用于计算数据类型或变量所占内存的字节数。它返回一个size_t类型的值,表示其操作数所占的字节数。 在使用时,sizeof()可以接收一个数据类型作为参数,也可以接收一个…...
Python学习笔记13 - 元组
什么是元组 元组的创建方式 为什么要将元组设计为不可变序列? 元组的遍历...
[leetcode]remove-duplicates-from-sorted-list-ii
. - 力扣(LeetCode) 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head [1,2,3,3,4,4,5] 输出:[1,2,5]示例 2&…...
共享内存和Pytorch中的Dataloader结合
dataloader中通常使用num_workers来指定多线程来进行数据的读取。可以使用共享内存进行加速。 代码地址:https://github.com/POSTECH-CVLab/point-transformer/blob/master/util/s3dis.py 文章目录 1. 共享内存和dataloader结合1.1 在init中把所有的data存储到共享内…...
分享 WebStorm 2024 激活的方案,支持JetBrains全家桶
大家好,欢迎来到金榜探云手! WebStorm公司简介 JetBrains 是一家专注于开发工具的软件公司,总部位于捷克。他们以提供强大的集成开发环境(IDE)而闻名,如 IntelliJ IDEA、PyCharm、和 WebStorm等。这些工具…...
Android OOM问题定位、内存优化
一、OOM out of memory:简称OOM,内存溢出,申请的内存大于剩余的内存而抛出的异常。 对于Android平台,广义的OOM主要是以下几种类型 JavaNativeThread 线程数的上限默认为32768,部分华为设备的限制是500通常1000左右…...
棋盘(c++题解)
题目描述 有一个m m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。 任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的) ,你只能向上、下、 左、右…...
滑动窗口例题
一、209:长度最小的子数组 209:长度最小的子数组 思路:1、暴力解法:两层for循环遍历,当sum > target时计算子数组长度并与result比较,取最小的更新result。提交但是超出了时间限制。 class Solution {public int minSubArray…...
智过网:注册安全工程师注册有效期与周期解析
在职业领域,各种专业资格认证不仅是对从业者专业能力的认可,也是保障行业安全、规范发展的重要手段。其中,注册安全工程师证书在安全生产领域具有举足轻重的地位。那么,注册安全工程师的注册有效期是多久呢?又是几年一…...
腐蚀Rust 服务端搭建架设个人社区服务器Windows教程
腐蚀Rust 服务端搭建架设个人社区服务器Windows教程 大家好我是艾西,一个做服务器租用的网络架构师也是游戏热爱者。最近在steam发现rust腐蚀自建的服务器以及玩家还是非常多的,那么作为服务器供应商对这商机肯定是不会放过的哈哈哈! 艾西这…...
蓝桥杯备赛:考前注意事项
考前注意事项 1、DevCpp添加c11支持 点击 工具 - 编译选项 中添加: -stdc112、万能头文件 #include <bits/stdc.h>万能头文件的缺陷:y1 变量 在<cmath>中用过了y1变量。 #include <bits/stdc.h> using namespace std;// 错误示例 …...
111111111111
111111111111...
uniapp 卡片勾选
前言 公司的app项目使用的uniapp,项目里有一个可勾选的卡片功能,效果图如下: 找了一圈没找到什么太好的组件,于是就自己简单写了一个,记录一下。避免以后还会用到 代码 <template><view class"card-…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法
用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...
【阅读笔记】MemOS: 大语言模型内存增强生成操作系统
核心速览 研究背景 研究问题:这篇文章要解决的问题是当前大型语言模型(LLMs)在处理内存方面的局限性。LLMs虽然在语言感知和生成方面表现出色,但缺乏统一的、结构化的内存架构。现有的方法如检索增强生成(RA…...
Easy Excel
Easy Excel 一、依赖引入二、基本使用1. 定义实体类(导入/导出共用)2. 写 Excel3. 读 Excel 三、常用注解说明(完整列表)四、进阶:自定义转换器(Converter) 其它自定义转换器没生效 Easy Excel在…...
