gateway 整合 spring security oauth2

news2024/11/29 6:37:20

微服务分布式认证授权方案

在分布式授权系统中,授权服务要独立成一个模块做统一授权,无论客户端是浏览器,app或者第三方,都会在授权服务中获取权限,并通过网关访问资源

OAuth2的四种授权模式

授权码模式

授权服务器将授权码(AuthorizationCode)转经浏览器发送给client,客户端拿着授权码向授权服务器索要访问access_token,这种模式是四种模式中最安全的一种模式。一般用于Web服务器端应用或第三方的原生App调用资源服务的时候

密码模式

资源拥有者将用户名、密码发送给客户端,客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌(access_token),密码模式使用较多,适应于第一方的单页面应用以及第一方的原生App

客户端模式

客户端向授权服务器发送自己的身份信息,并请求令牌(access_token),确认客户端身份无误后,将令牌(access_token)发送给client,这种模式是最方便但最不安全的模式。因此这就要求我们对client完全的信任,而client本身也是安全的。因此这种模式一般用来提供给我们完全信任的服务器端服务

简化模式

资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,授权服务器将授权码将令牌(access_token)以Hash的形式存放在重定向uri的fargment中发送给浏览器

 spring security 部分请看:spring security 认证授权

OAuth2的配置

  • 添加配置类,添加@EnableAuthorizationServer 注解标注这是一个认证中心
  • 继承 AuthorizationServerConfigurerAdapter
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 令牌存储策略
     */
    @Autowired
    private TokenStore tokenStore;

    /**
     * 客户端存储策略,这里使用内存方式,后续可以存储在数据库
     */
    @Autowired
    private ClientDetailsService clientDetailsService;

    /**
     * Security的认证管理器,密码模式需要用到
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private OAuthServerAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private DataSource dataSource;

    /**
     * 配置客户端详情,并不是所有的客户端都能接入授权服务
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      
        //TODO 暂定内存模式,后续可以存储在数据库中,更加方便
   clients.inMemory()
                //客户端id
                .withClient("myjszl")
                //客户端秘钥
                .secret(new BCryptPasswordEncoder().encode("123"))
                //资源id,唯一,比如订单服务作为一个资源,可以设置多个
                .resourceIds("res1")
                //授权模式,总共四种,1. authorization_code(授权码模式)、password(密码模式)、client_credentials(客户端模式)、implicit(简化模式)
                //refresh_token并不是授权模式,
                .authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")
                //允许的授权范围,客户端的权限,这里的all只是一种标识,可以自定义,为了后续的资源服务进行权限控制
                .scopes("all")
                //false 则跳转到授权页面
                .autoApprove(false)
                //授权码模式的回调地址
                .redirectUris("http://www.baidu.com");
    }

    /**
     * 令牌管理服务的配置
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        //客户端端配置策略
        services.setClientDetailsService(clientDetailsService);
        //支持令牌的刷新
        services.setSupportRefreshToken(true);
        //令牌服务
        services.setTokenStore(tokenStore);
        //access_token的过期时间
        services.setAccessTokenValiditySeconds(60 * 60 * 24 * 3);
        //refresh_token的过期时间
        services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);

        //设置令牌增强,使用JwtAccessTokenConverter进行转换
        services.setTokenEnhancer(jwtAccessTokenConverter);
        return services;
    }


    /**
     * 授权码模式的service,使用授权码模式authorization_code必须注入
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
        //todo 授权码暂时存在内存中,后续可以存储在数据库中
//        return new InMemoryAuthorizationCodeServices();
    }

    /**
     * 配置令牌访问的端点
     */
    @Override
    @SuppressWarnings("ALL")
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        //将自定义的授权类型添加到tokenGranters中
        List<TokenGranter> tokenGranters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
        tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices(), clientDetailsService,
                new DefaultOAuth2RequestFactory(clientDetailsService)));

        endpoints
                //设置异常WebResponseExceptionTranslator,用于处理用户名,密码错误、授权类型不正确的异常
                .exceptionTranslator(new OAuthServerWebResponseExceptionTranslator())
                //授权码模式所需要的authorizationCodeServices
                .authorizationCodeServices(authorizationCodeServices())
                //密码模式所需要的authenticationManager
                .authenticationManager(authenticationManager)
                //令牌管理服务,无论哪种模式都需要
                .tokenServices(tokenServices())
                //添加进入tokenGranter
                .tokenGranter(new CompositeTokenGranter(tokenGranters))
                //只允许POST提交访问令牌,uri:/oauth/token
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    /**
     * 配置令牌访问的安全约束
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        //自定义ClientCredentialsTokenEndpointFilter,用于处理客户端id,密码错误的异常
        OAuthServerClientCredentialsTokenEndpointFilter endpointFilter = new OAuthServerClientCredentialsTokenEndpointFilter(security,authenticationEntryPoint);
        endpointFilter.afterPropertiesSet();
        security.addTokenEndpointAuthenticationFilter(endpointFilter);

        security
                .authenticationEntryPoint(authenticationEntryPoint)
                //开启/oauth/token_key验证端口权限访问
                .tokenKeyAccess("permitAll()")
                //开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("permitAll()");
                //一定不要添加allowFormAuthenticationForClients,否则自定义的OAuthServerClientCredentialsTokenEndpointFilter不生效
//                .allowFormAuthenticationForClients();
    }
}

主要配置点:

  • 先配置客户端详细信息ClientDetailService   configure(ClientDetailsServiceConfigurer clients)
  • 在配置令牌访问端点(需要先配置令牌管理服务和tokenStore表示令牌的存放和管理策略)configure(AuthorizationServerEndpointsConfigurer endpoints)  配置加入令牌管理服务和其他oauth2.0需要的管理器和service
  • 最后配置访问令牌的约束条件,configure(AuthorizationServerSecurityConfigurer security)定义哪些端点可以访问 

spring security的配置 

  • 定义配置类,添加@EnableWebSecurity 注解
  • 继承WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SmsCodeSecurityConfig smsCodeSecurityConfig;

    /**
     * 加密算法
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //注入自定义的授权配置类
                .apply(smsCodeSecurityConfig)
                .and()
                .authorizeRequests()
                //注销的接口需要放行
                .antMatchers("/oauth/logout").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf()
                .disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //从数据库中查询用户信息
        auth.userDetailsService(userDetailsService);
    }



    /**
     * AuthenticationManager对象在OAuth2认证服务中要使用,提前放入IOC容器中
     * Oauth的密码模式需要
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

配置内容:

  • 定义加密算法,定义查询用户信息方案,在oauth2中密码模式会用到授权管理器
  • 定义访问策略,哪些放行,哪些需要授权 

JWT令牌配置 

不透明令牌则是令牌本身不存储任何信息,比如一串UUID,InMemoryTokenStore就类似这种

因此资源服务拿到这个令牌必须调调用认证授权服务的接口进行令牌的校验,高并发的情况下延迟很高,性能很低

透明令牌本身就存储这部分用户信息,比如JWT,资源服务可以调用自身的服务对该令牌进行校验解析,不必调用认证服务的接口去校验令牌

JwtAccessTokenConverter令牌增强类,用于JWT令牌和OAuth身份进行转换,内部生成JWT令牌,封装进入OAuth2AccessToken对象返回

在gateway中也需要有jwt令牌和tokenstore配置,代码相同



/**
 * 
 * 令牌的配置
 */
@Configuration
public class AccessTokenConfig {
    /**
     * 令牌的存储策略
     */
    @Bean
    public TokenStore tokenStore() {
        //使用JwtTokenStore生成JWT令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * JwtAccessTokenConverter
     * TokenEnhancer的子类,在JWT编码的令牌值和OAuth身份验证信息之间进行转换。
     * TODO:后期可以使用非对称加密
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenEnhancer();
        // 设置秘钥
        converter.setSigningKey(TokenConstant.SIGN_KEY);
        /*
         * 设置自定义得的令牌转换器,从map中转换身份信息
         * fix(*):修复刷新令牌无法获取用户详细信息的问题
         */
        converter.setAccessTokenConverter(new JwtEnhanceAccessTokenConverter());
        return converter;
    }

    /**
     * JWT令牌增强,继承JwtAccessTokenConverter
     * 将业务所需的额外信息放入令牌中,这样下游微服务就能解析令牌获取
     */
    public static class JwtAccessTokenEnhancer extends JwtAccessTokenConverter {
        /**
         * 重写enhance方法,在其中扩展
         */
        @Override
        public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
            Object principal = authentication.getUserAuthentication().getPrincipal();
            if (principal instanceof SecurityUser){
                //获取userDetailService中查询到用户信息
                SecurityUser user=(SecurityUser)principal;
                //将额外的信息放入到LinkedHashMap中
                LinkedHashMap<String,Object> extendInformation=new LinkedHashMap<>();
                //设置用户的userId
                extendInformation.put(TokenConstant.USER_ID,user.getUserId());
                //添加到additionalInformation
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(extendInformation);
            }
            return super.enhance(accessToken, authentication);
        }
    }
}

gateway认证管理器 

JwtAuthenticationManager,需要实现ReactiveAuthenticationManager这个接口。

认证管理的作用就是获取传递过来的令牌,对其进行解析、验签、过期时间判定 

/**
 * 
 * JWT认证管理器,主要的作用就是对携带过来的token进行校验,比如过期时间,加密方式等
 * 一旦token校验通过,则交给鉴权管理器进行鉴权
 */
@Component
@Slf4j
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {
    /**
     * 使用JWT令牌进行解析令牌
     */
    @Autowired
    private TokenStore tokenStore;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                .filter(a -> a instanceof BearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                .map(BearerTokenAuthenticationToken::getToken)
                .flatMap((accessToken -> {
                    OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);
                    //根据access_token从数据库获取不到OAuth2AccessToken
                    if (oAuth2AccessToken == null) {
                        return Mono.error(new InvalidTokenException("无效的token!"));
                    } else if (oAuth2AccessToken.isExpired()) {
                        return Mono.error(new InvalidTokenException("token已过期!"));
                    }
                    OAuth2Authentication oAuth2Authentication = this.tokenStore.readAuthentication(accessToken);
                    if (oAuth2Authentication == null) {
                        return Mono.error(new InvalidTokenException("无效的token!"));
                    } else {
                        return Mono.just(oAuth2Authentication);
                    }
                })).cast(Authentication.class);
    }
}

gateway 鉴权管理器

经过认证管理器JwtAuthenticationManager认证成功后,就需要对令牌进行鉴权,如果该令牌无访问资源的权限,则不允通过

JwtAccessManager,实现ReactiveAuthorizationManager



/**
 *
 * 鉴权管理器 用于认证成功之后对用户的权限进行鉴权
 * TODO 此处的逻辑:从redis中获取对应的uri的权限,与当前用户的token的携带的权限进行对比,如果包含则鉴权成功
 *      企业中可能有不同的处理逻辑,可以根据业务需求更改鉴权的逻辑
 *
 */
@Slf4j
@Component
@Deprecated
public class JwtAccessManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        //从Redis中获取当前路径可访问角色列表
        URI uri = authorizationContext.getExchange().getRequest().getURI();
        Object value = redisTemplate.opsForHash().get(SysConstant.OAUTH_URLS, uri.getPath());
        List<String> authorities = Convert.toList(String.class,value);
        //认证通过且角色匹配的用户可访问当前路径
        return mono
                //判断是否认证成功
                .filter(Authentication::isAuthenticated)
                //获取认证后的全部权限
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                //如果权限包含则判断为true
                .any(authorities::contains)
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

}

gateway security oauth2.0配置

  • 添加@EnableWebFluxSecurity注解,由于spring cldou gateway使用的Flux,因此需要使用@EnableWebFluxSecurity注解开启
  • 配置SecurityWebFilterChain添加跨域认证异常鉴权配置
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    /**
     * JWT的鉴权管理器
     */
    @Autowired
    private ReactiveAuthorizationManager<AuthorizationContext> accessManager;

    /**
     * token过期的异常处理
     */
    @Autowired
    private RequestAuthenticationEntryPoint requestAuthenticationEntryPoint;

    /**
     * 权限不足的异常处理
     */
    @Autowired
    private RequestAccessDeniedHandler requestAccessDeniedHandler;

    /**
     * 系统参数配置
     */
    @Autowired
    private SysParameterConfig sysConfig;

    /**
     * token校验管理器
     */
    @Autowired
    private ReactiveAuthenticationManager tokenAuthenticationManager;

    @Autowired
    private CorsFilter corsFilter;

    @Bean
    SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception{
        //认证过滤器,放入认证管理器tokenAuthenticationManager
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());

        http
                .httpBasic().disable()
                .csrf().disable()
                .authorizeExchange()
                //白名单直接放行
                .pathMatchers(ArrayUtil.toArray(sysConfig.getIgnoreUrls(),String.class)).permitAll()
                //其他的请求必须鉴权,使用鉴权管理器
                .anyExchange().access(accessManager)
                //鉴权的异常处理,权限不足,token失效
                .and().exceptionHandling()
                .authenticationEntryPoint(requestAuthenticationEntryPoint)
                .accessDeniedHandler(requestAccessDeniedHandler)
                .and()
                // 跨域过滤器
                .addFilterAt(corsFilter, SecurityWebFiltersOrder.CORS)
                //token的认证过滤器,用于校验token和认证
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
        return http.build();
    }
}

gateway全局过滤器

校验token,并将通过认证授权的请求转发下层服务,并将token信息放到请求头中


/**
 *  1、白名单直接放行
 *  2、校验token
 *  3、读取token中存放的用户信息
 *  4、重新封装用户信息,加密成功json数据放入请求头中传递给下游微服务
 */
@Component
@Slf4j
public class GlobalAuthenticationFilter implements GlobalFilter, Ordered {
    /**
     * JWT令牌的服务
     */
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 系统参数配置
     */
    @Autowired
    private SysParameterConfig sysConfig;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestUrl = exchange.getRequest().getPath().value();
        //1、白名单放行,比如授权服务、静态资源.....
        if (checkUrls(sysConfig.getIgnoreUrls(),requestUrl)){
            return chain.filter(exchange);
        }

        //2、 检查token是否存在
        String token = getToken(exchange);
        if (StringUtils.isBlank(token)) {
            return invalidTokenMono(exchange);
        }

        //3 判断是否是有效的token
        OAuth2AccessToken oAuth2AccessToken;
        try {
            //解析token,使用tokenStore
            oAuth2AccessToken = tokenStore.readAccessToken(token);
            Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
            //令牌的唯一ID
            String jti=additionalInformation.get(TokenConstant.JTI).toString();
            /**查看黑名单中是否存在这个jti,如果存在则这个令牌不能用****/
            Boolean hasKey = stringRedisTemplate.hasKey(SysConstant.JTI_KEY_PREFIX + jti);
            if (hasKey)
                return invalidTokenMono(exchange);
            //取出用户身份信息
            String user_name = additionalInformation.get("user_name").toString();
            //获取用户权限
            List<String> authorities = (List<String>) additionalInformation.get("authorities");
            //从additionalInformation取出userId
            String userId = additionalInformation.get(TokenConstant.USER_ID).toString();
            JSONObject jsonObject=new JSONObject();
            jsonObject.put(TokenConstant.PRINCIPAL_NAME, user_name);
            jsonObject.put(TokenConstant.AUTHORITIES_NAME,authorities);
            //过期时间,单位秒
            jsonObject.put(TokenConstant.EXPR,oAuth2AccessToken.getExpiresIn());
            jsonObject.put(TokenConstant.JTI,jti);
            //封装到JSON数据中
            jsonObject.put(TokenConstant.USER_ID, userId);
            //将解析后的token加密放入请求头中,方便下游微服务解析获取用户信息
            String base64 = Base64.encode(jsonObject.toJSONString());
            //放入请求头中
            ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header(TokenConstant.TOKEN_NAME, base64).build();
            ServerWebExchange build = exchange.mutate().request(tokenRequest).build();
            return chain.filter(build);
        } catch (InvalidTokenException e) {
            //解析token异常,直接返回token无效
            return invalidTokenMono(exchange);
        }


    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 对url进行校验匹配
     */
    private boolean checkUrls(List<String> urls,String path){
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String url : urls) {
            if (pathMatcher.match(url,path))
                return true;
        }
        return false;
    }

    /**
     * 从请求头中获取Token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isBlank(tokenStr)) {
            return null;
        }
        String token = tokenStr.split(" ")[1];
        if (StringUtils.isBlank(token)) {
            return null;
        }
        return token;
    }

    /**
     * 无效的token
     */
    private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {
        return buildReturnMono(ResultMsg.builder()
                .code(ResultCode.INVALID_TOKEN.getCode())
                .msg(ResultCode.INVALID_TOKEN.getMsg())
                .build(), exchange);
    }


    private Mono<Void> buildReturnMono(ResultMsg resultMsg, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = JSON.toJSONString(resultMsg).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset:utf-8");
        return response.writeWith(Mono.just(buffer));
    }
}

本文参照项目:spring-security-oauth2 

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

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

相关文章

【原创】统信UOS如何安装最新版Node.js(20.x)

注意直接使用sudo apt install nodejs命令安装十有八九会预装10.x的老旧版本Node.js&#xff0c;如果已经安装的建议删除后安装如下方法重装。 在统信UOS系统中更新Node.js可以通过以下步骤进行&#xff1a; 1. 卸载当前版本的Node.js 首先&#xff0c;如果系统中已经安装了N…

Maven进阶——坐标、依赖、仓库

目录 1.pomxml文件 2. 坐标 2.1 坐标的概念 2.2 坐标的意义 2.3 坐标的含义 2.4 自己项目的坐标 2.5 第三方项目坐标 3. 依赖 3.1 依赖的意义 3.2 依赖的使用 3.3 第三方依赖的查找方法 3.4 依赖范围 3.5 依赖传递和可选依赖 3.5.1 依赖传递 3.5.2 依赖范围对传…

推荐一个开源非线性视频编辑器:Kdenlive

Kdenlive是一个开源的视频编辑软件&#xff0c;项目始于约2003年。它基于Qt和KDE框架库构建&#xff0c;大部分视频处理由MLT框架完成&#xff0c;同时依赖其他开源项目如FFmpeg、frei0r、movit、ladspa、sox等。 软件特点&#xff1a; - 多轨视频编辑&#xff1a;支持多个音频…

大数据新视界 -- 大数据大厂之大数据和增强现实(AR)结合:创造沉浸式数据体验

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

2024 睿抗机器人开发者大赛(RAICOM)-【网络安全】CTF 部分WP

文章目录 一、前言二、MICS你是黑客么循环的压缩包Goodtime 三、WEBpy 四、Crypto变异凯撒RSAcrypto3 一、前言 WP不完整&#xff0c;仅供参考&#xff01; 除WEB&#xff0c;RE&#xff0c;PWN外&#xff0c;其余附件均已打包完毕 也是一个对MISC比较友好的一个比赛~ 123网…

springboot066人事系统(论文+源码)_kaic

基于vue的人事系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;作为学校以及一些培训机构&#xff0c;都在用信息化战术来部署线上学习以及线上考试&#xff0c;可以与线下的考试有机的结合在一起&#xff0c;实现基于vue的人事系统在技术上已成熟。本文介绍…

u盘装win10系统提示“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”解决方法

我们在u盘安装原版win10 iso镜像时&#xff0c;发现在选择硬盘时提示了“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”&#xff0c;直接导致了无法继续安装下去。出现这种情况要怎么解决呢&#xff1f;下面小编分享u盘安装win10系统提示“windows无法安装到这个磁盘…

信息安全工程师(53)网络安全审计机制与实现技术

前言 网络安全审计机制是指为了保护网络安全并发现潜在风险和漏洞而进行的一系列审计活动。审计的目的是检查并评估网络系统的安全性&#xff0c;以确保其符合相关法律法规和安全标准。 一、网络安全审计机制的重要性 网络安全审计机制对于保护组织的信息资产和敏感数据至关重要…

简单的 curl HTTP的POSTGET请求以及ip port连通性测试

简单的 curl HTTP的POST&GET请求以及ip port连通性测试 1. 需求 我们公司有一个演示项目&#xff0c;需要到客户那边进行项目部署&#xff0c;项目部署完成后我们需要进行项目后端接口的测试功能&#xff0c;但是由于客户那边么有条件安装类似于postman这种的测试工具&am…

CSS3文本阴影、文本换行、文本溢出、文本修饰、文本描边的使用

1.文本阴影&#xff1a;text-shadow 2.文本换行&#xff1a; white-space&#xff1a;pre&#xff08;可以理解为按原文显示&#xff09; white-space&#xff1a;pre-wrap&#xff08;不会超出父容器&#xff09; 3.文本溢出 text-overflow&#xff1a;ellipsis一般配合文本…

人工智能的未来:变革生活与工作的新篇章

随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;我们正站在一个前所未有的变革时代。AI不仅重新定义了医疗、企业运营和日常生活的各个方面&#xff0c;还潜移默化地改变着我们的思维方式和工作习惯。本文将深入探讨人工智能技术的应用前景&#xff0c;以及…

web前端-html:简单创建表格表单相结合的网页

效果&#xff1a; <body><form action"这里如果为空表单提交后不会有任何操作"method"get"<label for"edit">用户名</label><input type"text" name"用户名" id"最好不要空&#xff0c;id属性…

【MySQL】详解MySQL数据类型

一、数据类型 各类型的数值范围&#xff1a; 在MySQL中&#xff0c;整型可以指定是有符号的和无符号的&#xff0c;默认是有符号的。 可以通过UNSIGNED来说明某个字段是无符号的。对于int类型可能存放不下的数据&#xff0c;尽量不使用unsigned&#xff0c;unsigned int 同样可…

分布式链路追踪-01初步认识SkyWalking

一 SkyWaling是什么&#xff1f; Skywalking是分布式系统的应用程序性能监视工具&#xff0c;专为微服务、云原生架构和基于容器&#xff08;Docker、K8s、Mesos&#xff09;架构而设计。SkyWalking 是观察性分析平台和应用性能管理系统&#xff0c;提供分布式追踪、服务网格遥…

信发软件之电脑版拖动——未来之窗行业应用跨平台架构

一、电脑版拖动 二、电脑版随意移动函数 var _movefalse;//移动标记 var _x,_y;//鼠标离控件左上角的相对位置 $("#"宿主id).click(function(){ }).mousedown(function(e){ _movetrue; _xe.pageX-parseInt($("#"宿主id).css("left")); _ye…

Spring Boot论坛网站:安全特性与性能优化

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

计算机网络(十二) —— 高级IO

#1024程序员节 | 征文# 目录 一&#xff0c;预备 1.1 重新理解IO 1.2 五种IO模型 1.3 非阻塞IO 二&#xff0c;select 2.1 关于select 2.2 select接口参数解释 2.3 timeval结构体和fd_set类型 2.4 socket就绪条件 2.5 select基本工作流程 2.6 简单select的服务器代…

论文解读 | ECCV2024 AutoEval-Video:一个用于评估大型视觉-语言模型在开放式视频问答中的自动基准测试...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 陈修元&#xff0c;上海交通大学清源研究院硕士生 概述 总结来说&#xff0c;我们提出了一个新颖且具有挑战性的基准测试AutoEvalVideo&#xff0c;用于全…

蓝牙资讯|iOS 18.1 正式版下周推送,AirPods Pro 2耳机将带来助听器功能

苹果公司宣布将在下周发布 iOS 18.1 正式版&#xff0c;同时确认该更新将为 AirPods Pro 2 耳机带来新增“临床级”助听器功能。在启用功能后&#xff0c;用户首先需要使用 AirPods 和 iPhone 进行简短的听力测试&#xff0c;如果检测到听力损失&#xff0c;系统将创建一项“个…

5G RedCap工业路由器赋能电力物联网应用

随着5G轻量化技术应用的推进&#xff0c;5G RedCap旨在提供低功耗、低成本、广覆盖等功能特点赋能电力智能化升级。特别适用于工业物联网、低空经济、车联网、消费电子和轻量级5G的需求。 5G RedCap工业路由器的特点 低功耗&#xff1a;5G RedCap工业路由器通过节能技术&#…