十、Spring Boot集成Spring Security之HTTP请求授权

news2024/11/29 19:33:52

文章目录

  • 往期回顾:Spring Boot集成Spring Security专栏及各章节快捷入口
  • 前言
  • 一、HTTP请求授权工作原理
  • 二、HTTP请求授权配置
    • 1、添加用户权限
    • 2、配置ExceptionTranslationFilter自定义异常处理器
    • 3、HTTP请求授权配置
  • 三、测试接口
    • 1、测试类
    • 2、测试
  • 四、总结

往期回顾:Spring Boot集成Spring Security专栏及各章节快捷入口

  • Spring Boot集成Spring Security专栏
  • 一、Spring Boot集成Spring Security之自动装配
  • 二、Spring Boot集成Spring Security之实现原理
  • 三、Spring Boot集成Spring Security之过滤器链详解
  • 四、Spring Boot集成Spring Security之认证流程
  • 五、Spring Boot集成Spring Security之认证流程2
  • 六、Spring Boot集成Spring Security之前后分离认证流程最佳方案
  • 七、Spring Boot集成Spring Security之前后分离认证最佳实现
  • 八、Spring Boot集成Spring Security之前后分离认证最佳实现对接测试
  • 九、Spring Boot集成Spring Security之授权概述
  • 十、Spring Boot集成Spring Security之HTTP请求授权

前言

本文介绍HTTP请求授权工作原理、配置及适用场景,配合以下内容观看效果更佳!!!

  • 什么是授权,授权有哪些流程,Spring Security的授权配置有几种?请查看九、Spring Boot集成Spring Security之授权概述
  • HTTP请求授权的实现原理是什么,如何配置HTTP请求授权?请查看十、Spring Boot集成Spring Security之HTTP请求授权
  • 方法授权的实现原理是什么,如何配置方法授权?请查看十一、Spring Boot集成Spring Security之方法授权
  • 如何实现基于RBAC模型的授权方式?请查看十二、Spring Boot集成Spring Security之基于RBAC模型的授权

一、HTTP请求授权工作原理

​ 基于Spring Security最新的Http请求授权讲解,不再使用旧版的请求授权

  1. 授权过滤器AuthorizationFilter获取认证信息
  2. 调用RequestMatcherDelegatingAuthorizationManager的check方法验证该用户是否具有该请求的授权
  3. RequestMatcherDelegatingAuthorizationManager根据配置的请求和授权关系校验用户是否具有当前请求的授权并返回授权结果
  4. AuthorizationFilter处理授权结果,授权成功则继续调用过滤器链,否则抛出AccessDeniedException异常
  5. 认证失败时,ExceptionTranslationFilter处理AccessDeniedException异常,如果是当前认证是匿名认证或者RememberMe认证则调用AuthenticationEntryPoint的commence方法,否则调用AccessDeniedHandler的handler方法
  6. 工作原理图如下

authorizationfilter

二、HTTP请求授权配置

1、添加用户权限

package com.yu.demo.spring.impl;

import com.yu.demo.entity.UserDetailsImpl;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    //@Autowired
    //private UserService userService;
    // @Autowired
    //private UserRoleService userRoleService;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //TODO 通过username从数据库中获取用户,将用户转UserDetails
        //User user = userService.getByUsername(username);
        //TODO 从数据库实现查询权限并转化为List<GrantedAuthority>
        //List<String> roleIds = userRoleService.listRoleIdByUsername(username);
        //List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roleIds.size());
        //roleIds.forEach(roleId -> grantedAuthorities.add(new SimpleGrantedAuthority(roleId)));
        //return new User(username, user.getPassword(), user.getEnable(), user.getAccountNonExpired(), user.getCredentialsNonExpired(), user.getAccountNonLocked(), user.getAuthorities());
        //测试使用,指定权限
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        //与hasXxxRole匹配时添加ROLE_前缀
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        //与hasXxxAuthority匹配时原始值
        grantedAuthorities.add(new SimpleGrantedAuthority("OPERATE"));
        //{noop}不使用密码加密器,密码123的都可以验证成功
        UserDetailsImpl userDetails = new UserDetailsImpl(username, "{noop}123", true, true, true, true, grantedAuthorities);
        //userDetails中设置token,该token只是实现认证流程,未使用jwt
        userDetails.setToken(UUID.randomUUID().toString());
        return userDetails;
    }

}

2、配置ExceptionTranslationFilter自定义异常处理器

  • 因AuthorizationFilter授权失败时会抛出异常,该异常由ExceptionTranslationFilter处理,所以要配置自定义的异常处理器。

  • 自定义AccessDeniedHandler和AuthenticationEntryPoint异常处理器(可以用一个类实现认证授权相关的所有接口,也可以使用多个类分别实现)。

package com.yu.demo.spring.impl;


import com.yu.demo.entity.ApiResp;
import com.yu.demo.entity.UserDetailsImpl;
import com.yu.demo.util.SpringUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

@Component
public class LoginResultHandler implements AuthenticationSuccessHandler, LogoutSuccessHandler, AuthenticationEntryPoint, AuthenticationFailureHandler, AccessDeniedHandler {

    /**
     * 登录成功
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) authentication;
        UserDetailsImpl userDetailsImpl = (UserDetailsImpl) usernamePasswordAuthenticationToken.getPrincipal();
        //token返回到前端
        SpringUtil.respJson(response, ApiResp.success(userDetailsImpl.getToken()));
    }

    /**
     * 登录失败
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.loginFailure());
    }

    /**
     * 登出成功
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.success());
    }

    /**
     * 未登录调用需要登录的接口时
     */
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.notLogin());
    }

    /**
     * 已登录调用未授权的接口时
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        SpringUtil.respJson(response, ApiResp.forbidden());
    }
}

  • 配置异常处理:
                //异常处理配置
                .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
                        //授权失败处理器(登录账号访问未授权的资源时)
                        .accessDeniedHandler(loginResultHandler)
                        //登录失败处理器(匿账号访问需要未授权的资源时)
                        .authenticationEntryPoint(loginResultHandler))

3、HTTP请求授权配置

  • 本文使用最新的authorizeHttpRequests(AuthorizationFilter+AuthorizationManager)配置,不在使用authorizeRequests(FilterSecurityInterceptor+AccessDecisionManager+AccessDecisionVoter)
  • 请求和授权配置成对出现,配置在前的优先级更高
  • 请求种类
    • antMatchers:Ant风格的路径模式,?(匹配一个字符)、*(匹配零个或多个字符,但不包括目录分隔符)、**(匹配零个或多个目录)
    • mvcMatchers:Spring MVC的路径模式,支持路径变量和请求参数
    • regexMatchers:正则表达式路径模式
    • requestMatchers:实现RequestMatcher自定义匹配逻辑
    • anyRequest:未匹配的其他请求,只能有一个且只能放在最后
  • 授权种类
    • permitAll:匿名或登录用户都允许访问
    • denyAll:匿名和登录用户都不允许访问
    • hasAuthority:有配置的权限允许访问,AuthorityAuthorizationManager校验
    • hasRole:有配置的角色允许访问,ROLE_{配置角色}与用户权限匹配,AuthorityAuthorizationManager校验
    • hasAnyAuthority:有配置的任意一个权限的允许访问,AuthorityAuthorizationManager校验
    • hasAnyRole:有配置的任意一个角色允许访问,ROLE_{配置角色}与用户权限匹配,AuthorityAuthorizationManager校验
    • authenticated:已认证(不包括匿名)的允许访问,AuthenticatedAuthorizationManager校验
    • access:自定义授权处理
  • 因authorizeHttpRequests不支持使用anonymous()的方式配置匿名访问,未自定义匿名角色时可以通过hasRole(“ANONYMOUS”)或者hasAuthority(“ROLE_ANONYMOUS”)或其他类似的方式实现允许匿名请求的设置

image-20241122143255925

  • http请求授权配置
                //http请求授权
                .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
                        //不允许访问
                        .antMatchers("/test/deny")
                        .denyAll()
                        //允许匿名访问
                        .antMatchers("/test/anonymous")
                        .hasRole("ANONYMOUS")
                        //允许访问
                        .antMatchers("/test/permit")
                        .permitAll()
                        //测试使用:拥有ADMIN角色
                        .antMatchers("/test/admin")
                        //拥有ROLE_ADMIN权限,配置的角色不能以ROLE_作为前缀
                        .hasRole("ADMIN")
                        //测试使用:拥有OPERATE权限
                        .antMatchers("/test/operate")
                        //拥有OPERATE权限
                        .hasAuthority("OPERATE")
                        //其他的任何请求
                        .anyRequest()
                        //需要认证,且不能是匿名
                        .authenticated())
  • 完整过滤器链配置
package com.yu.demo.config;

import com.yu.demo.spring.filter.RestfulLoginConfigurer;
import com.yu.demo.spring.filter.RestfulUsernamePasswordAuthenticationFilter;
import com.yu.demo.spring.impl.LoginResultHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.context.SecurityContextRepository;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    //登录参数用户名
    private static final String LOGIN_ARG_USERNAME = "username";
    //登录参数密码
    private static final String LOGIN_ARG_PASSWORD = "password";
    //登录请求类型
    private static final String LOGIN_HTTP_METHOD = HttpMethod.POST.name();
    //登录请求地址
    private static final String LOGIN_URL = "/login";
    //登出请求地址
    private static final String LOGOUT_URL = "/logout";

    @Autowired
    private LoginResultHandler loginResultHandler;
    @Autowired
    private SecurityContextRepository securityContextRepository;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                //禁用UsernamePasswordAuthenticationFilter、DefaultLoginPageGeneratingFilter、DefaultLogoutPageGeneratingFilter
                .formLogin(FormLoginConfigurer::disable)
                //禁用BasicAuthenticationFilter
                .httpBasic(HttpBasicConfigurer::disable)
                //禁用CsrfFilter
                .csrf(CsrfConfigurer::disable)
                //禁用SessionManagementFilter
                .sessionManagement(SessionManagementConfigurer::disable)
                //异常处理配置
                .exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer
                        //授权失败处理器(登录账号访问未授权的资源时)
                        .accessDeniedHandler(loginResultHandler)
                        //登录失败处理器(匿账号访问需要未授权的资源时)
                        .authenticationEntryPoint(loginResultHandler))
                //http请求授权
                .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
                        //不允许访问
                        .antMatchers("/test/deny")
                        .denyAll()
                        //允许匿名访问
                        .antMatchers("/test/anonymous")
                        .hasRole("ANONYMOUS")
                        //允许访问
                        .antMatchers("/test/permit")
                        .permitAll()
                        //测试使用:拥有ADMIN角色
                        .antMatchers("/test/admin")
                        //拥有ROLE_ADMIN权限,配置的角色不能以ROLE_作为前缀
                        .hasRole("ADMIN")
                        //测试使用:拥有OPERATE权限
                        .antMatchers("/test/operate")
                        //拥有OPERATE权限
                        .hasAuthority("OPERATE")
                        //其他的任何请求
                        .anyRequest()
                        //需要认证,且不能是匿名
                        .authenticated())
                //安全上下文配置
                .securityContext(securityContextCustomizer -> securityContextCustomizer
                        //设置自定义securityContext仓库
                        .securityContextRepository(securityContextRepository)
                        //显示保存SecurityContext,官方推荐
                        .requireExplicitSave(true))
                //登出配置
                .logout(logoutCustomizer -> logoutCustomizer
                        //登出地址
                        .logoutUrl(LOGOUT_URL)
                        //登出成功处理器
                        .logoutSuccessHandler(loginResultHandler)
                )
                //注册自定义登录过滤器的配置器:自动注册自定义登录过滤器;
                //需要重写FilterOrderRegistration的构造方法FilterOrderRegistration(){},在构造方法中添加自定义过滤器的序号,否则注册不成功
                .apply(new RestfulLoginConfigurer<>(new RestfulUsernamePasswordAuthenticationFilter(LOGIN_ARG_USERNAME, LOGIN_ARG_PASSWORD, LOGIN_URL, LOGIN_HTTP_METHOD), LOGIN_URL, LOGIN_HTTP_METHOD))
                //设置登录地址:未设置时系统默认生成登录页面,登录地址/login
                .loginPage(LOGIN_URL)
                //设置登录成功之后的处理器
                .successHandler(loginResultHandler)
                //设置登录失败之后的处理器
                .failureHandler(loginResultHandler);

        //创建过滤器链对象
        return httpSecurity.build();
    }

}

三、测试接口

1、测试类

package com.yu.demo.web;

import com.yu.demo.entity.ApiResp;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    public ApiResp hello() {
        return ApiResp.success("hello");
    }

    /**
     * 匿名允许访问接口地址
     */
    @GetMapping("/anonymous")
    public ApiResp anonymous() {
        return ApiResp.success("anonymous");
    }

    /**
     * 禁止访问接口地址
     */
    @GetMapping("/deny")
    public ApiResp deny() {
        return ApiResp.success("deny");
    }

    /**
     * 允许访问接口地址
     */
    @GetMapping("/permit")
    public ApiResp permit() {
        return ApiResp.success("permit");
    }

    /**
     * 拥有ADMIN角色或ROLE_ADMIN权限允许访问接口地址
     */
    @GetMapping("/admin")
    public ApiResp admin() {
        return ApiResp.success("admin");
    }

    /**
     * 拥有OPERATE权限的允许访问接口地址
     */
    @GetMapping("/operate")
    public ApiResp operate() {
        return ApiResp.success("operate");
    }

}

2、测试

  1. 登录获取token

image-20241122144120674

  1. admin接口测试

image-20241122144242734

  1. 其他接口不在一一测试,有疑问或问题评论或私聊

四、总结

  1. 授权是拿用户的权限和可以访问接口的权限进行匹配,匹配成功时授权成功,匹配失败时授权失败
  2. 用户的权限对象是SimpleGrantedAuthority,字符串属性role
  3. 接口的role权限会通过ROLE_{role}转化为SimpleGrantedAuthority及其字符串属性role
  4. 接口的authority权限会直接转化为SimpleGrantedAuthority及其字符串属性role
  5. 拥有ROLE_ANONYMOUS权限或者ANONYMOUS角色可以访问匿名接口
  6. 后续会讲使用HTTP请求授权+自定义AuthorizationManager方式实现基于RBAC权限模型,欢迎持续关注
  7. 源码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2249947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详细介绍HTTP与RPC:为什么有了HTTP,还需要RPC?

目录 一、HTTP 二、RPC 介绍 工作原理 核心功能 如何服务寻址 如何进行序列化和反序列化 如何网络传输 基于 TCP 协议的 RPC 调用 基于 HTTP 协议的 RPC 调用 实现方式 优点和缺点 使用场景 常见框架 示例 三、问题 问题一&#xff1a;是先有HTTP还是先有RPC&…

【Linux】 进程是什么

0. 什么是进程&#xff0c;为什么要有进程&#xff1f; 1.操作系统为了更好的管理我们的软硬件&#xff0c;抽象出了许多概念&#xff0c;其中比较有代表的就是进程了。通俗的来说操作系统为了更好的管理加载到内存的程序&#xff0c;故引入进程的概念。 2.在操作系统学科中用P…

[OpenHarmony5.0][Docker][环境]OpenHarmony5.0 Docker编译环境镜像下载以及使用方式

T. 已测试目录 主机类型主机版本Docker镜像版本结果WSL2Ubuntu22.04Ubuntu20.04PASSWSL2Ubuntu22.04Ubuntu18.04PASS R. 软硬件要求&#xff1a; 编译硬件需求&#xff1a;做多系统测试&#xff0c;磁盘500GB起步(固态)&#xff08;机械会卡死&#xff09;&#xff0c;内存3…

vue3-使用vite创建项目

vue-cli处于维护模式&#xff0c;也可以使用脚手架正常创建vue3项目&#xff0c;与vue2创建方式一致 官方推荐使用vite创建项目 vite 是新一代前端构建工具,官网地址轻量快速的热重载&#xff08;HMR&#xff09;&#xff0c;能实现极速的服务启动。对 TypeScript、JSX、CSS 等…

Java设计模式笔记(一)

Java设计模式笔记&#xff08;一&#xff09; &#xff08;23种设计模式由于篇幅较大分为两篇展示&#xff09; 一、设计模式介绍 1、设计模式的目的 让程序具有更好的&#xff1a; 代码重用性可读性可扩展性可靠性高内聚&#xff0c;低耦合 2、设计模式的七大原则 单一职…

Vue3+node.js实现登录

文章目录 前端代码实现后端代码实现跨域处理 前端代码实现 效果图 前端代码实现 <template><div class"login-container"><el-card class"login-card"><template #header><div class"card-header"><span>…

网络原理(一)—— http

什么是 http http 是一个应用层协议&#xff0c;全称为“超文本传输协议”。 http 自 1991 年诞生&#xff0c;目前已经发展为最主流使用的一种应用层协议。 HTTP 往往基于传输层的 TCP 协议实现的&#xff0c;例如 http1.0&#xff0c;http1.0&#xff0c;http2.0 http3 是…

Next.js-样式处理

#题引&#xff1a;我认为跟着官方文档学习不会走歪路 Next.js 支持多种为应用程序添加样式的方法&#xff0c;包括&#xff1a; CSS Modules&#xff1a;创建局部作用域的 CSS 类&#xff0c;避免命名冲突并提高可维护性。全局 CSS&#xff1a;使用简单&#xff0c;对于有传统…

navicat premium连接sqlserver

连接不上就双击安装图中的msi文件之后再连试试&#xff01;

Axure RP教程:创建高效用户界面和交互

Axure RP是一款广受好评的软件&#xff0c;专门用于设计精致的用户界面和交互体验。这款软件提供了众多UI控件&#xff0c;并根据它们的用途进行了分类。与此同时&#xff0c;国产的即时设计软件作为Axure的替代品&#xff0c;支持在线协作和直接在浏览器中使用&#xff0c;无需…

Qt导出Excel图表

目的 就是利用Qt导出Excel图表,如果直接画Excel 图表&#xff0c;比较麻烦些&#xff0c;代码写得也复杂了&#xff1b;而直接利用Excel模块就简单了&#xff0c;图表在模块当中已经是现成的了&#xff0c;Qt程序只更改数据就可以了&#xff0c;这篇文章就是记录一下利用模块上…

DeSTSeg: Segmentation Guided Denoising Student-Teacher for Anomaly Detection

DeSTSeg: Segmentation Guided Denoising Student-Teacher for Anomaly Detection 清华、苹果 个人感觉 Introduction 很自然的让读者理解作者问题的提出&#xff0c;也有例子直接证明了这个问题的存在&#xff0c;值得借鉴&#xff01;&#xff01; Related work写的也很不…

Java设计模式 —— 【创建型模式】工厂模式(简单工厂、工厂方法模式、抽象工厂)详解

文章目录 前言一、简单工厂&#xff08;静态工厂&#xff09;1、概述2、代码实现3、优缺点 二、工厂方法模式1、概述2、代码实现3、优缺点 三、抽象工厂模式1、概述2、代码实现3、优缺点 四、总结 前言 先看个案例&#xff1a;【手机和手机店】在没有工厂的时候&#xff0c;手…

利用Java爬虫获取阿里巴巴中国站跨境属性的详细指南

在全球化贸易的浪潮中&#xff0c;跨境电商正成为连接全球买家和卖家的重要桥梁。阿里巴巴中国站作为全球领先的B2B电子商务平台&#xff0c;提供了海量的商品信息&#xff0c;其中跨境属性信息对于跨境电商尤为重要。本文将详细介绍如何使用Java编写爬虫&#xff0c;从阿里巴巴…

「Qt Widget中文示例指南」如何为窗口实现流程布局?(二)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文将展示如何为不…

鸿蒙学习使用模拟器运行应用(开发篇)

文章目录 1、系统类型和运行环境要求2、创建模拟器3、启动和关闭模拟器4、安装应用程序包和上传文件QA:在Windows电脑上启动模拟器&#xff0c;提示未开启Hyper-V 1、系统类型和运行环境要求 Windows 10 企业版、专业版或教育版及以上&#xff0c;且操作系统版本不低于10.0.18…

Java后端如何进行文件上传和下载 —— 本地版

简介&#xff1a; 本文详细介绍了在Java后端进行文件上传和下载的实现方法&#xff0c;包括文件上传保存到本地的完整流程、文件下载的代码实现&#xff0c;以及如何处理文件预览、下载大小限制和运行失败的问题&#xff0c;并提供了完整的代码示例。 大体思路 1、文件上传 …

SqlServer强制转换函数TRY_CONVERT和TRY_CAST

SqlServer强制转换函数TRY_CONVERT和TRY_CAST的介绍和案例分享 1、本节内容 CAST 和 CONVERT TRY_CAST TRY_CONVERT 适用于&#xff1a; SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse Analytics 分析平台系统 (PDW)Microsoft Fabric 中的 SQL 分析端点Micro…

透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)

一、透视投影 1.方法概述 Perspective projection&#xff08;透视投影&#xff09;是一种模拟人眼观察三维空间物体时的视觉效果的投影方法。它通过模拟观察者从一个特定视点观察三维场景的方式来创建二维图像。在透视投影中&#xff0c;远处的物体看起来比近处的物体小&…

linux 中后端jar包启动不起来怎么回事 -bash: java: 未找到命令

一、用以下命令检查jdk版本 输入&#xff1a;java -version&#xff0c;如果JDK 环境变量没有配置&#xff0c;你会看到如下提示 二、配置jdk环境 1.先找到/etc/profile文件&#xff0c;然后在该文件最后面加上以下配置 export JAVA_HOME/usr/local/jdk-21.0.1 export PATH$…