【SpringBoot】之Security进阶使用(登陆授权)

news2024/9/25 7:20:16

 🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《SpringBoot开发之Security系列》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁


前言

        在上一期的博客中我们博客分享了Security的相关登陆验证的数据获取以及记住我复选框功能、还有CSRF防御等等。今天给大家带来的是基于上一期的博客分享,是对上一期博客的完善功能以及扩展,上期博客完成的是Security联合数据库登陆验证,以及密码加码、网页生成cookie存储用户信息,最后做了一个csrf接口防御。本期博客带来的是用户登陆后的权限管理以及分配。

一、用户权限集成

1. 代码调整

        在上一期博客中我们的接口实现类实现两个接口类,这种写法是不规格的,因此我们要把一个实现类移出到目录上。

 MyUserDetailsService.java

package com.yx.security.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.security.pojo.User;
import com.yx.security.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;

/**
 * com.yx.security.config
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/23
 */
@Component
public class MyUserDetailsService implements UserDetailsService {

//    实例化实体接口类
    @Autowired
    private IUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//                根据用户查询用户信息
        User user = userService.getOne(new QueryWrapper<User>().eq("username", username));
        return user;
    }

}

配置类调整

         我们之前是跳转页面,现在我们模拟前后端分离,从后台发送数据到前台上用于判断跳转页面。

        我们要想传说数据到前台需要我们借助下面两个工具类,这两个类的代码在之前的博客文章中有,有需要的请翻阅之前的博客文章。 

 

        配置类进行对应的编写

         然后我们再重新运行启动项目

        上述就是我们重新登陆后返回的用户信息。

2. 权限设置

MyUserDetailsService.java
package com.yx.security.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.security.pojo.*;
import com.yx.security.service.*;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;

import java.util.List;
import java.util.stream.Collectors;

/**
 * com.yx.security.config
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/23
 */
@Component
public class MyUserDetailsService implements UserDetailsService {

//    实例化实体接口类
    @Autowired
    private IUserService userService;
//    实例化实体接口类
//    用户角色接口
    @Autowired
    private IUserRoleService userRoleService;
//   角色接口
    @Autowired
    private IRoleService roleService;
//    角色权限接口
    @Autowired
    private IRoleModuleService roleModuleService;
//    权限接口类
    @Autowired
    private IModuleService moduleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//                根据用户查询用户信息
        User user = userService.getOne(new QueryWrapper<User>().eq("username", username));
//        判断是否存在用户
        if (user == null) {//不存在
            throw new UsernameNotFoundException("用户名不存在");
     }
//        先查询用户的身份根据用户的id,获取身份id
//        map遍历对象并且返回新的数据放到一个新的流中
//        collectors.toList()将新的流转换成list集合
        List<Integer> role_ids = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId()))
                .stream().map(UserRole::getRoleId)
                .collect(Collectors.toList());
//        根据身份id查询角色信息的名字字段
        List<String> roles = roleService
                .list(new QueryWrapper<Role>().in("role_id", role_ids))
                .stream().map(Role::getRoleName)
                .collect(Collectors.toList());
//        根据身份id具备的权限id
        List<Integer> module_ids = roleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", role_ids))
                .stream().map(RoleModule::getModuleId)
                .collect(Collectors.toList());
//      根据权限id对应的权限
        List<String> modules = moduleService.list(new QueryWrapper<Module>().in("id", module_ids))
                .stream().map(Module::getUrl)
                .collect(Collectors.toList());
//        拼接身份及权限
        roles.addAll(modules);
//        遍历集合获取信息生成新的集合
        List<SimpleGrantedAuthority> authorities = roles.stream()
                .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
//        将该集合添加到用户实体对象中的权限属性中
        user.setAuthorities(authorities);
        return user;
    }

}

        我们现在去登陆测试一下

          控制台也会显示对应的错误提示

         如下图所示我们在该位置标记端点进行运行测试

        解决的方案有两种,一种是对其数据库中的空值对其进行填写补充;另一种方法就是在后台对其进行空处理。 下面是在后端进行的空处理

         在配置类中加上一个注解 

         给指定的接口方法设置的权限。 

         下面是完整的代码

 IndexController.java
package com.yx.security.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class IndexController {


    @RequestMapping("/")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/index")
    public String toIndex(){
        return "accessDenied";
    }

    @ResponseBody
    @RequestMapping("/oreder_add")
    @PreAuthorize("hasAuthority('order:manager:add')")
    public String order_add(){
        return "订单新增";
    }

    @ResponseBody
    @RequestMapping("/book_add")
    @PreAuthorize("hasAuthority('book:manager:add')")
    public String book_add(){
        return "书籍新增";
    }

}
MyUserDetailsService.java
package com.yx.security.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.security.pojo.*;
import com.yx.security.service.*;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * com.yx.security.config
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/23
 */
@Component
public class MyUserDetailsService implements UserDetailsService {

//    实例化实体接口类
    @Autowired
    private IUserService userService;
//    实例化实体接口类
//    用户角色接口
    @Autowired
    private IUserRoleService userRoleService;
//   角色接口
    @Autowired
    private IRoleService roleService;
//    角色权限接口
    @Autowired
    private IRoleModuleService roleModuleService;
//    权限接口类
    @Autowired
    private IModuleService moduleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//                根据用户查询用户信息
        User user = userService.getOne(new QueryWrapper<User>().eq("username", username));
//        判断是否存在用户
        if (user == null) {//不存在
            throw new UsernameNotFoundException("用户名不存在");
     }
//        先查询用户的身份根据用户的id,获取身份id
//        map遍历对象并且返回新的数据放到一个新的流中
//        collectors.toList()将新的流转换成list集合
//        filter过滤器过滤掉null
        List<Integer> role_ids = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId()))
                .stream().map(UserRole::getRoleId)
                .collect(Collectors.toList());
//        根据身份id查询角色信息的名字字段
        List<String> roles = roleService
                .list(new QueryWrapper<Role>().in("role_id", role_ids))
                .stream().map(Role::getRoleName)
                .collect(Collectors.toList());
//        根据身份id具备的权限id
        List<Integer> module_ids = roleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", role_ids))
                .stream().map(RoleModule::getModuleId)
                .collect(Collectors.toList());
//      根据权限id对应的权限
        List<String> modules = moduleService.list(new QueryWrapper<Module>()
                        .in("id", module_ids))
                .stream().map(Module::getUrl)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
//        拼接身份及权限
        roles.addAll(modules);
//        遍历集合获取信息生成新的集合
        List<SimpleGrantedAuthority> authorities = roles.stream()
                .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
//        将该集合添加到用户实体对象中的权限属性中
        user.setAuthorities(authorities);
        return user;
    }

}
 WebSecurityConfig.java
package com.yx.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yx.security.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
//开启SpringSecurity的默认行为
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

//    引入密码加密类
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


//        实例化接口实现类
//        @Autowired
//        private UserServiceImpl userService;
        @Autowired
        private MyUserDetailsService userDetailsService;

//    实例化SpringBoot自带的json序列化操作了
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用(基于数据库方式)
     * @param
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        //创建DaoAuthenticationProvider
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        //设置userDetailsService,基于数据库方式进行身份认证
        provider.setUserDetailsService(userDetailsService);
        //配置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

//    实例化数据源
    @Autowired
    private DataSource dataSource;

    /**
     * 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 设置为true要保障数据库该表不存在,不然会报异常哦
        // 所以第二次打开服务器应用程序的时候得把它设为false
        tokenRepository.setCreateTableOnStartup(false);
        return tokenRepository;
    }



    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http)
            throws Exception{
         http.authorizeRequests()
                    // 开放接口访问权限,不需要登录就可以访问
                    .antMatchers("/").permitAll()
//                admin开头的路径全要只能由管理员进入
//                  .antMatchers("/admin/**").hasRole("ADMIN")
                    // 其余所有请求全部需要登陆才能访问
                    .anyRequest().authenticated()
                 .and()
                 .formLogin()
//                 设置登录页面的Url
                 .loginPage("/toLogin")
//                 设置处理登陆请求的接口
                 .loginProcessingUrl("/userLogin")
//                 设置用户名参数名与页面的一致
                 .loginProcessingUrl("/userLogin")
                 .usernameParameter("username")
                 .passwordParameter("password")
//                 成功处理器
                 .successHandler((req, resp, auth)->{
//                     resp.sendRedirect("/index");
                     objectMapper.writeValue(resp.getOutputStream(),auth.getPrincipal());
                 })
//                 设置登录失败跳转页面
                 .failureHandler((req, resp, ex) -> {
//                     设置错误消息
                     req.setAttribute("msg", ex.getMessage());
//                                          跳转到登录页面
                     req.getRequestDispatcher("/").forward(req, resp);
                 })
                .and()
//                 处理异常跳转其他页面
               .exceptionHandling()
                 .accessDeniedPage("/noAccess")
                 .and()
                 .logout()
                 // 设置安全退出的URL路径
                 .logoutUrl("/logout")
                 // 设置退出成功后跳转的路径
                 .logoutSuccessUrl("/toLogin")
                 .and()
                 .rememberMe()
                 // 指定 rememberMe 的参数名,用于在表单中携带 rememberMe 的值。
                 //.rememberMeParameter("remember-me")
                 // 指定 rememberMe 的有效期,单位为秒,默认2周。
                 .tokenValiditySeconds(30)
                 // 指定 rememberMe 的 cookie 名称。
                 .rememberMeCookieName("remember-me-cookie")
                 // 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现。
                 .tokenRepository(persistentTokenRepository())
                 // 指定 rememberMe 的认证方式,需要实现 UserDetailsService 接口,并在其中查询用户信息。
                 .userDetailsService(userDetailsService);
        http.csrf().disable();
    	return http.build();
    }
}

        运行测试代码

         由上图可知我们使用不同的身份进行登陆时,若该身份没有该权限进行访问一些方法时会出现权限报错。

二、自定义处理器

1. 自定义错误类型

        在JsonResponseStatus中进行自定义

 

2. 修改配置类

 

package com.yx.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yx.security.resp.JsonResponseBody;
import com.yx.security.resp.JsonResponseStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
//开启SpringSecurity的默认行为
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

//    引入密码加密类
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


//        实例化接口实现类
//        @Autowired
//        private UserServiceImpl userService;
        @Autowired
        private MyUserDetailsService userDetailsService;

//    实例化SpringBoot自带的json序列化操作了
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用(基于数据库方式)
     * @param
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        //创建DaoAuthenticationProvider
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        //设置userDetailsService,基于数据库方式进行身份认证
        provider.setUserDetailsService(userDetailsService);
        //配置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }

//    实例化数据源
    @Autowired
    private DataSource dataSource;

    /**
     * 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 设置为true要保障数据库该表不存在,不然会报异常哦
        // 所以第二次打开服务器应用程序的时候得把它设为false
        tokenRepository.setCreateTableOnStartup(false);
        return tokenRepository;
    }



    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http)
            throws Exception{
         http.authorizeRequests()
                    // 开放接口访问权限,不需要登录就可以访问
                    .antMatchers("/").permitAll()
//                admin开头的路径全要只能由管理员进入
//                  .antMatchers("/admin/**").hasRole("ADMIN")
                    // 其余所有请求全部需要登陆才能访问
                    .anyRequest().authenticated()
                 .and()
                 .formLogin()
//                 设置登录页面的Url
                 .loginPage("/toLogin")
//                 设置处理登陆请求的接口
                 .loginProcessingUrl("/userLogin")
//                 设置用户名参数名与页面的一致
                 .loginProcessingUrl("/userLogin")
                 .usernameParameter("username")
                 .passwordParameter("password")
//                 成功处理器
                 .successHandler((req, resp, auth)->{
//                     resp.sendRedirect("/index");
                     Object user = auth.getPrincipal();
                     objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.success(user));
                 })
//                 设置登录失败跳转页面
                 .failureHandler((req, resp, ex) -> {
//                     设置错误消息
                     objectMapper.writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));

                 })
                .and()
//                 处理异常跳转其他页面
               .exceptionHandling()
//                 .accessDeniedPage("/noAccess")
//                 进入到这里代表权限不足
                 .accessDeniedHandler((req, resp, ex)->{
//                     返回权限不足的提示
                     objectMapper.writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_ACCESS));
                 })
                 //没有认证   用户没有登陆
                 .authenticationEntryPoint((req, resp, ex) -> {
                     objectMapper
                             .writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));
                 })
                 .and()
                 .logout()
                 // 设置安全退出的URL路径
                 .logoutUrl("/logout")
                 // 设置退出成功后跳转的路径
                 .logoutSuccessUrl("/toLogin")
                 .and()
                 .rememberMe()
                 // 指定 rememberMe 的参数名,用于在表单中携带 rememberMe 的值。
                 //.rememberMeParameter("remember-me")
                 // 指定 rememberMe 的有效期,单位为秒,默认2周。
                 .tokenValiditySeconds(30)
                 // 指定 rememberMe 的 cookie 名称。
                 .rememberMeCookieName("remember-me-cookie")
                 // 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现。
                 .tokenRepository(persistentTokenRepository())
                 // 指定 rememberMe 的认证方式,需要实现 UserDetailsService 接口,并在其中查询用户信息。
                 .userDetailsService(userDetailsService);
        http.csrf().disable();
    	return http.build();
    }
}

3. 运行测试

        我们运行时由于某些原因导致的代码问题会提示相应的问题

         如果你的代码后面特别多建议移出来新建一个工具类

 MyAuthenticationFailureHandler.java
package com.yx.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yx.security.resp.JsonResponseBody;
import com.yx.security.resp.JsonResponseStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

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

/**
 * com.yx.security.config
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/24
 */
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    //    实例化SpringBoot自带的json序列化操作了
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//        设置错误消息
        objectMapper.writeValue(response.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));
    }
}

        后续可以在MyAuthenticationFailureHandler实现用户登陆次数过多用户禁用的功能


🎉🎉本期的博客分享到此结束🎉🎉

📚📚各位老铁慢慢消化📚📚

🎯🎯下期博客博主会带来新货🎯🎯

🎁三连加关注,阅读不迷路 !🎁

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

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

相关文章

C# Onnx yolov8 pokemon detection

目录 效果 模型信息 项目 代码 下载 C# Onnx yolov8 pokemon detectio 效果 模型信息 Model Properties ------------------------- date&#xff1a;2023-12-25T17:55:44.583431 author&#xff1a;Ultralytics task&#xff1a;detect license&#xff1a;AGPL-3.0 h…

4.9【共享源】流的多生产者和消费者

当一个系统中存在多个生产者和消费者时&#xff0c;情况可能会变得复杂。 了解生产者和消费者流之间支持的基数非常重要。 本质上&#xff0c;一个生产者流可以与多个消费者流连接&#xff0c;但一个消费者流只能连接到一个生产者流。请注意&#xff0c;基数关系仅限于单个流&…

竞赛保研 基于RSSI的室内wifi定位系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; wifi室内定位系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;…

asp.net mvc 重定向问题的解决方式

前端ajax发起请求&#xff0c;在后端接口中重定向&#xff0c;结果报错&#xff0c;无法跳转 Ajax实际上是通过XMLHttpRequest来向服务器发送异步请求的&#xff0c;从服务器获取数据&#xff0c;然后使用JS来更新页面&#xff0c;这也就是常说的局部刷新实现方式&#xff0c;所…

Linux部署MeterSphere结合内网穿透实现远程访问服务管理界面

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

华为数通方向HCIP-DataCom H12-831题库(多选题:241-249)

第241题 (NEW) 以下哪些操作可能会影响客户网络的正常运行? A、从设备上下载日志 B、软件升级 C、路由协议配置变更 D、debug核心交换机上转发的所有IP报文 答案:ABCD 解析: 第242题 对于防火墙的默认安全区 Trust 和 Untrust 的说法,正确的有 A、从 Trust 区域访问 Untr…

安卓开发--RecyclerView快速上手【上】

效果图展示: 下面三个kml文件名即动态从服务器获取并列表加载。 RecyclerView简称 RV, 是作为 ListView 和 GridView 的加强版出现的,目的是在有限的屏幕之上展示大量的内容,因此 RecyclerView 的复用机制的实现是它的一个核心部分。 一般在动态获取服务器数据进行…

K8S理论

kubernetes&#xff1a;8个字母省略&#xff0c;就是k8s 自动部署自动扩展和管理容器化部署的应用程序的一个开源系统 k8s是负责自动化运维管理多个容器化程序的集群&#xff0c;是一个功能强大的容器编排工具 分布式和集群化的方式进行容器化管理 版本有1.15 .1.18 .1.20 …

ES5语法数组遍历、字符串、对象新增方法

ES5数组遍历forEach\filter\some\every\map、字符串trim、对象keys\defineProperty新增方法   Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎&#xff0c;能够解决不断涌现出的各种用例。作为 Elastic Stack 的核心&#xff0c;它集中存储您的数据&#xff…

ssh工具 向指定的ssh服务器配置公钥

此文分享一个python脚本,用于向指定的ssh服务器配置公钥,以达到免密登录ssh服务器的目的。 效果演示 🔥完整演示效果 👇第一步,显然,我们需要选择功能 👇第二步,确认 or 选择ssh服务器 👇第三步,输入ssh登录密码,以完成公钥配置 👇验证,我们通过ssh登录…

PHP+MySQL组合开发:万能在线预约小程序源码系统 附带完整的搭建教程

近年来&#xff0c;线上服务逐渐成为市场主流。特别是在预约服务领域&#xff0c;用户越来越倾向于选择方便快捷的线上预约方式。传统的预约方式如电话预约和到店预约不仅效率低下&#xff0c;而且在信息传达上存在很大的误差。这使得用户常常需要反复确认&#xff0c;浪费了大…

java实现矩阵谱峰搜索算法

矩阵谱峰搜索算法&#xff0c;也称为矩阵谱峰查找算法&#xff0c;是一种用于搜索二维矩阵中谱峰的方法。谱峰是指在矩阵中的一个元素&#xff0c;它比其上下左右四个相邻元素都大或相等。 该算法的基本思想是从矩阵的中间列开始&#xff0c;找到该列中的最大元素&#xff0c;…

使用ImageJ将Raw格式图片批量转换为JPG

自动方法&#xff1a; 1&#xff0c;创建一个txt文本文档&#xff0c;然后将下面的代码复制粘贴进去。 2&#xff0c;将代码的第一行path修改为你的raw图片所在的路径, 3&#xff0c;第二行out修改为转换后jpg图片存储路径。 4&#xff0c;完成前2步后&#xff0c;如果你是win…

最新ChatGPT商业运营网站程序源码,支持Midjourney绘画,GPT语音对话+DALL-E3文生图+文档对话总结

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

华为云Windows Server服务器下,Node使用pm2-logrotate分割pm2日志,解决pm2日志内存占用过高的问题。

一、简介 PM2 是一个守护进程管理器&#xff0c;它将帮助您管理和保持您的应用程序在线。PM2 入门很简单&#xff0c;它以简单直观的 CLI 形式提供&#xff0c;可通过 NPM 安装。官网地址&#xff1a;https://pm2.keymetrics.io/ 二、问题&#xff1a;pm2日志内存占用过高&am…

《运维人员的未来:IT界的“万金油“如何继续闪耀光芒》

文章目录 每日一句正能量前言35岁被称为运维半衰期&#xff0c;究竟为何&#xff1f;如何顺利过渡半衰期运维的职业发展路径后记 每日一句正能量 凡事顺其自然&#xff0c;遇事处于泰然&#xff0c;得意之时淡然&#xff0c;失意之时坦然&#xff0c;艰辛曲折必然&#xff0c;历…

iOS - 真机调试的新经验

文章目录 获取真机 UDIDPlease reconnect the device.iOS 开发者模式Fetching debug symbols 很久没有在真机运行 iOS 测试了&#xff0c;今天帮忙调试&#xff0c;发现很多东西都变了&#xff0c;有些东西也生疏了&#xff0c;在这里记录下。 获取真机 UDID 创建Profile 需要…

【MySQL】:超详细MySQL完整安装和配置教程

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. MySQL数据库1.1 版本1.2 下载1.3 安装1.4 客户端连接 &#x1f324;️全篇总…

十八、本地配置Hive

1、配置MYSQL mysql> alter user rootlocalhost identified by Yang3135989009; Query OK, 0 rows affected (0.00 sec)mysql> grant all on *.* to root%; Query OK, 0 rows affected (0.00 sec)mysql> flush privileges; Query OK, 0 rows affected (0.01 sec)2、…

王老师的会计电算化课IT问题集锦

机房无法联网&#xff0c;无法登录扣扣&#xff0c;微信如何截图 Prt Scr 键盘上找到 Print Screen 键&#xff0c;按一下桌面左下角 Win 图标用鼠标点一下&#xff0c;输入 mspaint&#xff0c;或者调出画图在画图软件中按键盘上的 Ctrl V 组合键屏幕图片就贴到了画图软件里…