java技术:spring-secrity实现认证、授权

目录

一、依赖

二、逻辑图

三、代码设计

1、WebSecurityConfigurerAdapter的实现类

2、设计登录接口

config配置:

1)UserDetailsService实现类重写:

2)书写登录实现类(调用authenticationManager、可以与后面的逻辑连贯起来)

四、认证加授权

五、工具类:(可网上随便找了用)

jwt

redis

redis序列化:

六、逻辑梳理

1)security有个Authentication

2)密码校验


一、依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
//        <dependency>
//            <groupId>org.springframework.cloud</groupId>
//            <artifactId>spring-cloud-starter-oauth2</artifactId>
//        </dependency>

二、逻辑图

图1

图2

三、代码设计

1、WebSecurityConfigurerAdapter的实现类

package com.example.config;

import com.example.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author Mr.M
 * @version 1.0
 * @description 安全管理配置
 * @date 2022/9/26 20:53
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) //开启资源授权配置 权限和资源是两码事
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//    @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    //配置用户信息服务
//    @Bean
//    public UserDetailsService userDetailsService() {
//        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
//        return manager;
//    }
//

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    //引入Bean AuthenticationManager 供调用
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance()
        //spring用于加密的一个算法
        return new BCryptPasswordEncoder();
    }

    //配置安全拦截机制
    //__________________这里的所有路径都是不包含上下文路径的
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/login").anonymous()//放行接口 未登录才能访问
                .antMatchers().permitAll()
                //.anonymous()未登录 已登录不可访问
                //.authenticated()已登录
                //.permitAll()未登录已登录都可访问
                //接口权限配置
                .antMatchers("/getUser/*").hasAnyAuthority("getUser2")
                .anyRequest().authenticated();//其它全部需要认证
//                .and()
//                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
                //认证过滤器
                http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);



                //security允许跨域
                http.cors();

    }




}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

将AuthenticationManager注入bean 实现自己设计的登录接口与auth流程的衔接

   @Bean
    public PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance()
        //spring用于加密的一个算法
        return new BCryptPasswordEncoder();
    }

加密方式,如果不注入会在密码比对环节出问题

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/**").anonymous()//放行接口
                .anyRequest().authenticated();//其它全部需要认证
//                .and()
//                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success

//     http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

加载security前会加载的过滤器 可设置校验的接口匹配 这里的.antMatchers("/**").anonymous()时放行所有 既所有接口都会进入auth校验

ps:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
                .anyRequest().permitAll()//其它请求全部放行
                .and()
                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
    }
 

2、设计登录接口

改写两部分 一部分是UserDetailsService(默认是从内存传用户名密码到UserDetails对象)

改成:自己设计UserDetailsService的实现类从数据库加载

二部分是,重写的登录接口,不用默认的(返回JWT令牌,并将token存入redis缓存)

ps:token的claim最好用普通字段 不然可能会出现实体类解析失败 这里存userId(因为redis序列化会破坏实体类、序列化最好是List<简单类型>)

config配置:

package com.example.config;

import com.example.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author Mr.M
 * @version 1.0
 * @description 安全管理配置
 * @date 2022/9/26 20:53
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) //开启资源授权配置 权限和资源是两码事
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//    @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    //配置用户信息服务
//    @Bean
//    public UserDetailsService userDetailsService() {
//        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
//        return manager;
//    }
//

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    //引入Bean AuthenticationManager 供调用
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance()
        //spring用于加密的一个算法
        return new BCryptPasswordEncoder();
    }

    //配置安全拦截机制
    //__________________这里的所有路径都是不包含上下文路径的
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/login").anonymous()//放行接口 未登录才能访问
                .antMatchers().permitAll()
                //.anonymous()未登录 已登录不可访问
                //.authenticated()已登录
                //.permitAll()未登录已登录都可访问
                //接口权限配置
                .antMatchers("/getUser/*").hasAnyAuthority("getUser2")
                .anyRequest().authenticated();//其它全部需要认证
//                .and()
//                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
                //认证过滤器
                http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);



                //security允许跨域
                http.cors();

    }




}

ps:配置有过滤对象,授权权限、跨域(spring跨域+security跨域)

spring跨域配置

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.FormContentFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Mr.Lan
 * @version 1.0
 * @ClassName CrosConfig$
 * @description Springboot跨域配置 等同nacos配置(如网关) 但也需要SpringSecurity配置
 * @date 2024/5/19 0:32
 **/
@Configuration
public class CrosConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token")
                .allowedMethods("*")
                .allowedOrigins("*")
                .allowCredentials(true);
    }

    /**
     * 支持PUT、DELETE请求
     */
    @Bean
    public FormContentFilter httpPutFormContentFilter() {
        return new FormContentFilter();
    }

}
1)UserDetailsService实现类重写:
package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.entity.LoginUser;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.checkerframework.checker.units.qual.A;
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.Service;
import org.springframework.util.ObjectUtils;

import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author Mr.Lan
 * @version 1.0
 * @ClassName UserDetailsServiceImpl$
 * @description TODO
 * @date 2024/5/17 15:52
 **/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //传参是用户姓名
        //数据库查询用户信息以及权限信息
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>();
        userLambdaQueryWrapper.eq(User::getUsername,s);
        User user = userMapper.selectOne(userLambdaQueryWrapper);
        if( ObjectUtils.isEmpty(user)){
           throw new RuntimeException("用户不存在");
        }
        //查询权限信息
        ArrayList<String> permissions = new ArrayList<>(Arrays.asList("getUser", "getUser1"));
        LoginUser loginUser = new LoginUser(user,permissions);
        //返回UserDeatil对象
        //返回接口的实现类相当于返回了接口
        return loginUser;//返回后后面会校验密码
    }
}

ps:对于封装成UserDetails接口返回:可以返回实现类(接口的实例)

两种情况:

第一种 直接new实现类 然后将自己的参数传进去封装起来

第二种  创建实现类 将自己的参数与实现类联系起来

这里是第二种:

package com.example.entity;

import com.alibaba.fastjson.annotation.JSONField;
import com.example.entity.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Mr.Lan
 * @version 1.0
 * @ClassName LoginUser$
 * @description LoginUser充当中间人实现UserDetails与User的关系
 * @date 2024/5/17 16:10
 **/
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {
    private User user;

    private ArrayList<String> permisssions;


    @JSONField(serialize = false)
    private List<SimpleGrantedAuthority> permisssionsList;

    public User getUser() {
        return user;
    }

    public LoginUser(User user, ArrayList<String> permisssions) {
        this.user = user;
        this.permisssions = permisssions;
    }

    public LoginUser(User user) {
        this.user = user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
//        List<GrantedAuthority> collect = new ArrayList<>();
//        for (String permisssion : this.permisssions) {
//            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permisssion);
//            collect.add(simpleGrantedAuthority);
//        }
//        List<GrantedAuthority> collect = this.permisssions.stream().map(permisssion -> {
//            return new SimpleGrantedAuthority(permisssion);
//        }).collect(Collectors.toList());
        if (permisssionsList != null) {
//            return this.permisssionsList;//加this表示当前对象的属性 不加表示这个类中字段的值
            return permisssionsList;
        }

        this.permisssionsList = this.permisssions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        return permisssionsList;


    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    //是否有效(没过期)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

2)书写登录实现类(调用authenticationManager、可以与后面的逻辑连贯起来)
package com.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.MapUtils;
import com.alibaba.spring.util.ObjectUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.LoginUser;
import com.example.entity.Result;
import com.example.entity.User;
import com.example.service.UserService;
import com.example.mapper.UserMapper;
import com.example.utils.RedisTemplateUtils;
import io.jsonwebtoken.Claims;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapProperties;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.example.utils.JwtUtils.generateJwt;

/**
* @author Admin
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-05-16 21:03:01
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    UserMapper userMapper;
    @Autowired
    RedisTemplateUtils redisTemplateUtils;
    //authenticationManager在login的方法(这里是实现类)调用,就继续传递
    @Override
    public Result login(User user) {
        //当参数是接口时可以传接口的实现类 创建实现类封装传递 Authentication
        //new UsernamePasswordAuthenticationToken()的两个参数 Object 后面要用
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
        //这里将返回的时认证后的结果
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("认证失败");
        }
        BeanUtils.copyProperties(authenticate.getPrincipal(),user);
        //获取返回中的信息 加密返回token
        HashMap<String, Object> claims = new HashMap<>();
        LoginUser principal = (LoginUser)authenticate.getPrincipal();
        claims.put("userId",principal.getUser().getId());
        String token = generateJwt(claims);
        //将token存入redis 并以userId为key
        redisTemplateUtils.set("login:"+principal.getUser().getId(),principal,432000L);
        HashMap<String, String> map = new HashMap<>();
        map.put("token",token);
//        String jsonString = JSON.toJSONString(map);
        return Result.success(400,"登陆成功",map);
    }

    @Override
    public Result loginOut() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser principal = (LoginUser)authentication.getPrincipal();
        String userId = principal.getUser().getId();
        //删除redis中的token
        try {
            redisTemplateUtils.del("login:"+userId);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("退出登录失败");
        }
        return Result.success("成功退出登录");
    }
}




登录接口:

package com.example.web;

import com.example.entity.Result;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import com.example.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.omg.PortableInterceptor.USER_EXCEPTION;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

import static com.example.utils.JwtUtils.generateJwt;

/**
 * @author Mr.Lan
 * @version 1.0
 * @ClassName LoginController$
 * @description TODO
 * @date 2024/5/16 21:05
 **/
@RestController
@Slf4j
public class LoginController {
    @Autowired
    UserService userService;
    @GetMapping("/getUser/{id}")
//    @PreAuthorize("hasAuthority('getUser2')")//需要权限才能访问
    User getUser(@PathVariable Long id){
        User user = userService.getById(id);
        log.info("User:{}",user);
        return user;
    }

    @RequestMapping("/login-success")
    public String loginSuccess() {
        return "登录成功";
    }
//    @GetMapping("/login-success")
//    public String loginSuccess() {
//
//        return "登录成功";
//    }

    @RequestMapping("/generateJwt")
    public String Jwt() {
        HashMap<String,Object> claim=new HashMap<String,Object>();
        claim.put("name","admin");
        claim.put("password","admin");
        String token = generateJwt(claim);
        return token;

    }


    @PostMapping("/login")
    public Result login(@RequestBody User user) {
        return userService.login(user);
    }


    @GetMapping("/loginOut")
    public Result loginOut() {
        return userService.loginOut();
    }








}

四、认证加授权

在配置类中将该过滤器置于UsernamePasswordAuthenticationFilter过滤器之前

package com.example.filter;

import com.example.entity.LoginUser;
import com.example.utils.JwtUtils;
import com.example.utils.RedisTemplateUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.rmi.server.ExportException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.example.utils.JwtUtils.parseJWT;

/**
 * @author Mr.Lan
 * @version 1.0
 * @ClassName JwtAuthenticationTokenFilter$
 * @description token中存用户信息 方便获取 提高查询效率 完成授权 而tJWT就是通过userId校验
 * @date 2024/5/17 21:27
 **/
@Slf4j
@Component
//@WebFilter(urlPatterns = "/*")
public class JwtAuthenticationTokenFilter implements Filter {

    @Autowired
    RedisTemplateUtils redisTemplateUtils;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("初始化");
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest Request, ServletResponse Response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest servletRequest = (HttpServletRequest) Request;
        HttpServletResponse servletResponse = (HttpServletResponse) Response;
        //获取请求头的token
        String token = servletRequest.getHeader("token");
        if(StringUtils.isEmpty(token)){
            //未登录放行
            filterChain.doFilter(Request,Response);
        }
        Map<String, Object> claims=null;
        try {
             claims = parseJWT(token);
        } catch (Exception e) {
            throw new RuntimeException("token异常");
        }
        String userId = claims.get("userId").toString();
        String key="login:"+userId;
        //获取缓存的key看是否过期关键快速是获取用户的一些信息
        //一般认证和授权分开的 授权的token从redis获取
        LoginUser loginUser = (LoginUser)redisTemplateUtils.get(key);
        if(Objects.isNull(loginUser)){
            throw new RuntimeException("token非法");
        }
        //authorities存认证信息
        //todo LoginUser保护用户信息和权限信息
        UsernamePasswordAuthenticationToken info = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(info);
        //已登录放行
        filterChain.doFilter(Request,Response);
    }

    @Override
    public void destroy() {
        log.info("结束");
        Filter.super.destroy();

    }
}

ps:在封装SecurityContextHolder中加入权限信息,后面的FilterSecurityInterceptor会根据接口所需权限进行拦截

权限可用注解或者配置类中配置

五、工具类:(可网上随便找了用)

jwt

package com.example.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

/**
 * @author Mr.Lan
 * @version 1.0
 * @ClassName JwtUUtils$
 * @description TODO
 * @date 2024/5/17 12:30
 **/
public class JwtUtils {
    private static String signKey = "lanjie";//签名密钥
    private static Long expire = 43200000L; //有效时间

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)//自定义信息(有效载荷)
                .signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
                .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)//指定签名密钥
                .parseClaimsJws(jwt)//指定令牌Token
                .getBody();
        return claims;
    }
}

redis

package com.example.utils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;

/**
 * @author 陈振东
 * 基于spring和redis的redisTemplate工具类 针对所有的hash 都是以h开头的方法 针对所有的Set 都是以s开头的方法 不含通用方法 针对所有的List 都是以l开头的方法
 */
@Component
@Slf4j
public class RedisTemplateUtils {

  @Resource
  private RedisTemplate<String, Object> redisTemplate;

    // =============================1-common============================

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    // ============================2-String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }

    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

//    /**
//     * 递增 适用场景: https://blog.csdn.net/y_y_y_k_k_k_k/article/details/79218254 高并发生成订单号,秒杀类的业务逻辑等。。
//     *
//     * @param key 键
//     * @param   要增加几(大于0)
//     * @return
//     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

//    /**
//     * 递减
//     *
//     * @param key 键
//     * @param by  要减少几(小于0)
//     * @return
//     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================3-Map=================================

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================4-set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            log.error(key, e);
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    // ============================5-zset=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return
     */
    public Set<Object> zSGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            log.error(key, e);
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean zSHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    public Boolean zSSet(String key, Object value, double score) {
        try {
            return redisTemplate.opsForZSet().add(key, value, 2);
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long zSSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return
     */
    public long zSGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long zSetRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }
    // ===============================6-list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始 0 是第一个元素
     * @param end   结束 -1代表所有值
     * @return
     * @取出来的元素 总数 end-start+1
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            log.error(key, e);
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            log.error(key, e);
            return null;
        }
    }
//
//    /**
//     * 将list放入缓存
//     *
//     * @param key   键
//     * @param value 值
//     * @param time  时间(秒)
//     * @return
//     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

//    /**
//     * 将list放入缓存
//     *
//     * @param key   键
//     * @param value 值
//     * @param time  时间(秒)
//     * @return
//     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            log.error(key, e);
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            log.error(key, e);
            return 0;
        }
    }

}

redis序列化:

package com.example.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.util.IOUtils;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

/**
 * {@link RedisSerializer} FastJson Generic Impl
 * @author lihengming
 * @since 1.2.36
 */
public class RedisSerialization implements RedisSerializer<Object> {
    private final static ParserConfig defaultRedisConfig = new ParserConfig();
    static { defaultRedisConfig.setAutoTypeSupport(true);}

    public byte[] serialize(Object object) throws SerializationException {
        if (object == null) {
            return new byte[0];
        }
        try {
            return JSON.toJSONBytes(object, SerializerFeature.WriteClassName);
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
        }
    }

    public Object deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            return JSON.parseObject(new String(bytes, IOUtils.UTF8), Object.class, defaultRedisConfig);
        } catch (Exception ex) {
            throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
        }
    }
}

六、逻辑梳理

这里用的springSecurity实现了用户的登录、认证、授权

1、登录

自定义登录接口实现了第一层的过滤器,包括了密码校验,以及拥有的权限

登录是通过改写过滤器,

 利用authenticationManager.authenticate(usernamePasswordAuthenticationToken);

引入authenticate串联起来整个secrity登录过程

2、认证

认证接口实现,首先认证是通过新增过滤器(同时加权限)。对token进行校验(是否有效以及是否过期等)并设置security上下文

下面介绍两个变量

1)security的上下文 SecurityContextHolder(存放用户的账户、密码、权限信息)

存:
UsernamePasswordAuthenticationToken info = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(info);

1、存入两个参数(一般用户名密码)

2、存三个(前面两个自定义,后面的是授权信息)

取:

通过Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

其中SecurityContextHolder 是 Spring Security 框架提供的一个重要的类,用于保存和获取当前请求的安全上下文信息。

2)密码校验  UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());

这里存入输入的用户名和密码

这里输入的是数据库用户 但是在loginUser中重写了后去用户名密码是从User中获取 因此相当于传入了用户名密码,后面会在图2的第七步校验用户名密码。

ps:oauth2协议可以更加方便的实现认证授权以及分布式认证授权。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MariaDB 给指定列值自动加密(持久数据加触发器)

文章目录 代码插入时&#xff0c;自动加密更新时&#xff0c;自动加密查看触发器数据操作示例update数据取出解密取 注意一次尝试&#xff0c;看加密后数据长度 参考链接&#xff1a; 一篇非常好的讲解触发器的文章&#xff1a;示例、原理MySQL/MariaDB触发器。 用触发器自动加…

Android App启动流程和源码详解

前言 之前看了些App启动流程的文章&#xff0c;但是看得很浅显&#xff0c;隔了没多久就忘了&#xff0c;自己抓耳挠腮的终于看完了&#xff0c;看得头疼哦。因为很多是个人理解&#xff0c;大哥们主打一个7分信&#xff0c;2分思考&#xff0c;1分怀疑哈。 主要看的源码是An…

CPP Con 2020:Type Traits I

先谈谈Meta Programming 啥是元编程呢&#xff1f;很简单&#xff0c;就是那些将其他程序当作数据来进行处理和传递的编程&#xff08;私人感觉有点类似于函数式&#xff1f;&#xff09;这个其他程序可以是自己也可以是其他程序。元编程可以发生在编译时也可以发生在运行时。…

27寸2K显示器 - HKC G27H2

HKC G27H2是一款面向电竞市场的高性能显示器&#xff0c;以其2K分辨率和180Hz的刷新率作为主要卖点&#xff0c;旨在为玩家提供流畅而清晰的视觉体验。配备HDR 400技术和95% DCI-P3色域覆盖&#xff0c;这款显示器还支持升降旋转支架&#xff0c;为用户提供了高度的人体工程学适…

微软开源多模态大模型Phi-3-vision,微调实战来了

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型& AIGC 技术趋势、大模型& AIGC 落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了…

继承初级入门复习

注意&#xff1a;保护和私有在类中没有区别&#xff0c;但是在继承中有区别&#xff0c;private在继承的子类不可见&#xff0c;protect在继承的子类可见 记忆方法&#xff1a;先看基类的修饰符是private&#xff0c;那都是不可见的。如果不是&#xff0c;那就用继承的修饰和基…

知了传课Flask学习(持续更新)

一、基础内容 1.Flask快速应用 pip install flask from flask import Flaskapp Flask(__name__)app.route(/) def index():return Hello worldif __name__ __main__:app.run() 2.debug、host、port配置 from flask import Flask,requestapp Flask(__name__)app.route(/) d…

在洁净实验室设计装修中怎么选择合适实验室家具?

在现代科学研究和技术开发中&#xff0c;洁净实验室装修设计成为了确保实验准确性和安全性的重要因素。洁净实验室需要提供一个无尘、无菌、受控的环境&#xff0c;而在洁净实验室装修设计这个过程中&#xff0c;如何选择合适的实验室家具就显得尤为重要&#xff0c;因为它直接…

第一行代码 按书配置Menu不出来

问题&#xff1a;按照书本配置Menu&#xff0c;就是不出来 页面activity 源码 重写了&#xff1a;onCreateOptionsMenu(), onOptionsItemSelected() package com.example.lanidemoktimport android.os.Bundle import android.util.Log import android.view.Menu import andro…

欢乐钓鱼大师攻略大全,游戏自动辅助,钓鱼大全!

欢迎来到《欢乐钓鱼大师》的攻略大全&#xff01;本文将为你详细介绍游戏中的各类玩法、技巧和注意事项&#xff0c;帮助你快速掌握游戏精髓&#xff0c;成为一名真正的钓鱼大师。攻略内容包括新手鱼竿选择、锦标赛攻略、实用技巧、藏宝图玩法、箱子开法等多个方面。让我们一起…

个人博客网站开发笔记3

文章目录 前言p4 Front Matterp5 配置文件p6 命令p7 部署新的教学视频部署博客到github找视频教程也是一个技能详细步骤安装主题安装渲染器修改主题创建gitub仓库生成密钥验证密钥是否匹配修改config文件推送到github 前言 主要是安装啥的比较费劲 现在已经比较简单了感觉 之…

面试问题小结

说说你的项目&#xff0c;从里面学到啥了&#xff08;随便说&#xff09; CAS 线程池 的各个方面 线程咋创建&#xff08;4种方式&#xff09; 说一下聚集索引和非聚集索引 50w男 50w女 &#xff0c;在B树中咋存储的&#xff08;类似下面的图&#xff0c;变通一下就行了&a…

WXML模板语法-事件绑定

一、 1.事件 事件是渲染层到逻辑层的通讯方式&#xff0c;通过事件可以将用户在渲染层产生的行为&#xff0c;反馈到逻辑层进行业务的处理 2.小程序中常用的事件 3.事件对象的属性列表 当事件回调触发的时候&#xff0c;会收到一个事件对象event&#xff0c;其属性为&#x…

一文带你入门ini格式

引入: 以蜂鸣器为例&#xff0c;每次我们增加新的设备&#xff0c; 都需要添加两个新文件: 修改程序代码&#xff0c;手动添加: 缺点: 不利于维护 设备类节点直接通过ini文件配置 什么是.ini文件 ini文件通常以纯文本形式存在&#xff0c;并且包含了一个或多个节&#xff08;se…

快速搭建流媒体服务

1、安装流媒体服务 源码地址&#xff1a;https://gitee.com/ossrs/srs 本次采用docker安装 docker run --rm -it -p 1935:1935 -p 1985:1985 -p 8080:8080 -p 8000:8000/udp -p 10080:10080/udp registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5 查看运行效果&#xff…

[LLM-Agents]浅析Agent工具使用框架:MM-ReAct

上文LLM-Agents]详解Agent中工具使用Workflow提到MM-ReAct框架&#xff0c;通过结合ChatGPT 与视觉专家模型来解决复杂的视觉理解任务的框架。通过设计文本提示&#xff08;prompt design&#xff09;&#xff0c;使得语言模型能够接受、关联和处理多模态信息&#xff0c;如图像…

QQ技术导航源码附带交易系统

网站功能 QQ登录 友联自助交换 友情链接交易功能 多功能搜索 ico小图标本地化 网站图片本地化 蜘蛛日志 文章评论 网站评论 自助链接匿名提交站点&#xff0c;添加友链访问网站自动审核通过 VIP 会员等级 VIP 付费升级 单个文章或者站点付费快审 多背景图片可自定义背景图片…

【数据结构】第七节:堆

个人主页&#xff1a; 深情秋刀鱼-CSDN博客 数据结构专栏&#xff1a;数据结构与算法 源码获取&#xff1a;数据结构: 上传我写的关于数据结构的代码 (gitee.com) ​ 目录 一、堆 1.堆的概念 2.堆的定义 二、堆的实现 1.初始化和销毁 2.插入 向上调整算法 3.删除 向下调整算法…

9.STL中list的常见操作(图文并茂)

目录 1.list的介绍及使用 1.1.list的构造 1.2 list iterator的使用 1.3. list capacity 1.4.list modifiers 1.5.list的迭代器失效 1.list的介绍及使用 list介绍 &#xff0c;可以通过以下图直观的感受到 vector 和 list 的区别 Vector 插入代价高&#xff0c;但便于排…

LabVIEW与串口通讯在运行一段时间后出现数据接收中断的问题

这些问题可能与硬件、软件或通信协议有关。以下是详细的原因分析和可能的解决方案&#xff1a; 一、硬件原因 串口线缆或接口问题&#xff1a; 由于长时间使用&#xff0c;串口线缆可能出现接触不良或损坏。接口松动也可能导致通讯中断。 解决方案&#xff1a;检查并更换串口…