SpringSecurity(JWT、SecurityConfig、Redis)

可能会报错Last unit does not have enough valid bits
把jwtUtils的KEY改成偶数位

主要用于校验,授权
导入依赖
<dependency)
<groupId>org.springframework. boot</groupId>
<artifactId>spring-boot-starter-security</artifactId)
</dependency>

导入以来会自动跳转登录页面,用户为user,密码在控制台,也就是打印日志记录
它的原理就是一个过滤器链,一个过滤器,多个就是过滤器链。

这里springboot版本用的是2.5.0
jdk版本也不要搞错了,一定是java8

fastjson是用于json转换的工具

<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>

<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

关于redis的相关配置Redis使用FastJson序列化
```
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
{

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    static
    {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }


    protected JavaType getJavaType(Class<?> clazz)
    {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}

```
Redis配置
```

@Configuration
public class RedisConfig {
    @Bean
    @SuppressWarnings(value = {"unchecked","rawtypes"})
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);


        //使用stringRedisserializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用stringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
}

```
统一返回值domain响应类
```
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> {
    /**
     * 状态码
     */
    private Integer code;
    /**
     * 提示信息,如果有错误时,前端可以获取该字段进行提示
     */
    private String msg;
    /**
     * 查询到的结果数据,
     */
    private T data;

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

```
JWT工具包

```
public class JwtUtil {

    //有效期为
    public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "fancy";

    public static String getUUID(){
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        return token;
    }
    
    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
        return builder.compact();
    }

    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("fancy")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);
    }

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
        return builder.compact();
    }

    public static void main(String[] args) throws Exception {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
        Claims claims = parseJWT(token);
        System.out.println(claims);
    }

    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    
    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }
}
```

RedisCache
```
package com.imot.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 删除Hash中的数据
     * 
     * @param key
     * @param hkey
     */
    public void delCacheMapValue(final String key, final String hkey)
    {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

```

WebUtils
```
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class WebUtils
{
    /**
     * 将字符串渲染到客户端
     * 
     * @param response 渲染对象
     * @param string 待渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string) {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }
}

```

这里注意如果在数据库中的密码前面加上{noop}即可让密码明文

重写一个UserDetailsServiceImpl去实现UserDetailsService重写方法loadUserByUsername,返回值是UserDetails

```

@Service
public class UserDetailsServiceImpl implements UserDetailsService {


    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //查询用户信息

        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getUserName,username);

        User user = userMapper.selectOne(userLambdaQueryWrapper);

        if (Objects.isNull(user)){
            throw new RuntimeException("用户名或密码错误");
        }
        //TODO 查询对应的权限信息


```

创建一个类LoginUser来实现返回值UserDetails

```
package com.imot.pojo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {

    private User user;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @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;
    }
}


```

加密方法为
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String  encode = passwordEncoder.encode("1234");

passwordEncoder.matches("明文","密文");    //可查看是否相等返回一个布尔值

```


```


因为已经封装好Bean的SecurityConfig,需要调用直接autowaired

jwt依赖
```
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>

</dependency>

```


**SecurityConfig**
```

package com.imot.config;

import com.imot.filter.JwtAuthenticationTokenFilter;
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.config.annotation.web.builders.HttpSecurity;
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;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

    }

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

```
jwtUtils工具中
createJWt("123")就是生成token的方法
这就是解析token,subject即是解密
Claims claims= parseJT("eyJhbGci0iJ“);
String subject = claims.getSubject();

在SecurityConfig中重写方法authenticationManagerBean,按住alt+insert

**写一个登录的方法**
```
    @PostMapping("/user/login")
    @ApiOperation("测试登录")
    public ResponseResult login(@RequestBody User user) {
        //登录


        return loginService.login(user);
    }
```

**LoginServiceImpl**
```
package com.imot.service.impl;

import com.imot.domain.ResponseResult;
import com.imot.pojo.entity.LoginUser;
import com.imot.pojo.entity.User;
import com.imot.service.LoginService;
import com.imot.utils.JwtUtil;
import com.imot.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Objects;

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;
    @Override
    public ResponseResult login(User user) {
        //AuthenticationManager进行用户认证
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());


        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

        //判断null就验证没有通过,不为null就认证通过了
        if (Objects.isNull(authenticate)){
            throw new RuntimeException("登陆失败");
        }

        //如果认证通过了,使用userid去生成一个jwt存入返回值responseResult返回

        //使用userid生成token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        //authenticate存入redis
        redisCache.setCacheObject("login:"+userId,loginUser);
        //把token响应给前端
        HashMap<String,String> map = new HashMap<>();
        map.put("token",jwt);

        return new ResponseResult(200,"登陆成功",map);
    //

    }
}

```

Jwt过滤器
```
package com.imot.filter;

import com.imot.pojo.entity.LoginUser;
import com.imot.utils.JwtUtil;
import com.imot.utils.RedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

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

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            //放行
            filterChain.doFilter(request, response);
            return;
        }
        //解析token
        String userid;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userid = claims.getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
        //从redis中获取用户信息
        String redisKey = "login:" + userid;
        LoginUser loginUser = redisCache.getCacheObject(redisKey);
        if(Objects.isNull(loginUser)){
            throw new RuntimeException("用户未登录");
        }
        //存入SecurityContextHolder
        //TODO 获取权限信息封装到Authentication中
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser,null,null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行
        filterChain.doFilter(request, response);
    }
}
```


退出controller
```
    @RequestMapping("/user/logout")
    @ApiOperation("测试退出")
    public ResponseResult logout(@RequestBody User user) {
        //登录


        return loginService.logout();
    }

```
serviceImpl
```

    @Override
    public ResponseResult logout() {
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userid = loginUser.getUser().getId();
        redisCache.deleteObject("login:"+userid);
        return new ResponseResult(200,"退出成功");
    }
```


 

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

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

相关文章

[Linux] TCP协议介绍(3): TCP协议的“四次挥手“过程、状态分析...

TCP协议是面向连接的 上一篇文章简单分析了TCP通信非常重要的建立连接的"三次握手"的过程 本篇文章来分析TCP通信中同样非常重要的断开连接的"四次挥手"的过程 TCP的"四次挥手" TCP协议建立连接 需要"三次握手". "三次挥手&q…

基于STM32和人工智能的自动驾驶小车系统

目录 引言环境准备自动驾驶小车系统基础代码实现&#xff1a;实现自动驾驶小车系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景&#xff1a;自动驾驶应用与优化问题解决方案与优化收尾与总结 1. 引言 随着人工智能和嵌入式系统技术的…

稀疏矩阵是什么 如何求

稀疏矩阵是一种特殊类型的矩阵&#xff0c;其中大多数元素都是零。由于稀疏矩阵中非零元素的数量远少于零元素&#xff0c;因此可以使用特定的数据结构和算法来高效地存储和处理它们&#xff0c;从而节省存储空间和计算时间。 RowPtr 数组中的每个元素表示对应行的第一个非零元…

变压器纵联差动保护的Simulink仿真

利用Simulink在变压器空载合闸励磁涌流的仿真模型的基础上将变压器改为采用Yd11联结且不考虑饱和特性,增加外部故障模块Fault2,得到新的仿真模型如图1所示。 图1 变压器的Simulink仿真模型 在建立模型时,请注意三相电压电流测量模块Um,UN的方向。比率制动特性纵差保护…

Docker 镜像****后,如何给Ubuntu手动安装 docker 服务

Docker 镜像****后&#xff0c;如何给Ubuntu手动安装 docker 服务 下载地址下载自己需要的安装包使用下面的命令进行安装启动服务 最近由于某些未知原因&#xff0c;国内的docker镜像全部被停。刚好需要重新安装自己的笔记本为双系统&#xff0c;在原来的Windows下&#xff0c;…

目标检测算法SSD与FasterRCNN

目标检测算法SSD与FasterRCNN SSD:&#xff08; Single Shot MultiBox Detector&#xff09;特点是在不同特征尺度上预测不同尺度的目标。 SSD网络结构 首先对网络的特征进行说明&#xff1a;输入的图像是300x300的三通道彩色图像。 网络的第一个部分贯穿到Vgg16模型 Conv5的…

工厂方法模式实战之某商场一次促销活动

目录 1.5.1、前言1.5.2、实战场景简介1.5.3、开发环境1.5.4、用传统的if-else语句实现1.5.4.1、工程结构1.5.4.2、if-else需求实现1.5.4.3、测试验证 1.5.5、工厂模式优化代码1.5.5.1、工程结构1.5.5.2、代码实现1.5.5.2.1、定义各种商品发放接口及接口实现1.5.5.2.2、定义工厂…

解释一下 Flux

Flux 是 Facebook 提出的应用架构模式&#xff0c;用于管理 React 应用中的数据流和状态。它强调单向数据流&#xff0c;以解决复杂应用中数据和视图不一致的问题。Flux 不是一个具体的库或框架&#xff0c;而是一种设计模式&#xff0c;围绕以下几个核心概念&#xff1a; Acti…

Android开发系列:高性能视图组件Surfaceview

一、Surfaceview概述 在Android应用开发领域&#xff0c;面对视频播放、游戏构建及相机实时预览等高性能需求场景&#xff0c;直接操控图像数据并即时展示于屏幕成为必要条件。传统View组件在此类情境下显现局限性&#xff1a; 性能瓶颈&#xff1a;传统View的绘制任务由UI主…

Spring系统学习 -Spring IOC 的XML管理Bean之类类型属性赋值、数组类型属性赋值、集合类属性赋值

类类型属性赋值&#xff1a; 在XML配置中&#xff0c;可以使用 <property> 元素为类类型属性赋值。通过指定属性的名称和值&#xff0c;可以直接将其他Bean的实例引用注入到目标Bean的属性中。这种方式可以建立对象之间的关联关系。例如&#xff0c;可以将一个Address对象…

vue学习(二)

9.vue中的数据代理 通过vm对象来代理data对象中的属性操作&#xff08;读写&#xff09;&#xff0c;目的是为了更加方便操作data中的数据 基本原理&#xff1a;通过Object.defineProperty()把data对象所有属性添加到vm上&#xff0c;为每一个添加到vm上的属性&#xff0c;都增…

比特币通用API服务

Bitcoin 通用API服务 exlectrs: API后台服务(Rust语言编写) https://github.com/Blockstream/electrs.git 使用electr作为后台的区块链浏览器:https://github.com/Blockstream/esplora.git https://github.com/Blockstream/electrs https://github.com/romanz/electrs/blo…

Element-UI - 解决el-table中图片悬浮被遮挡问题

在开发中&#xff0c;发现element-ui在el-table中添加图片悬浮显示时&#xff0c;会被单元格遮挡的问题。通过查询得到的解决办法&#xff0c;大多是修改.el-table类中相关样式属性&#xff0c;但经过验证发现会影响到其他正常功能的使用。对于此问题解决其实也并不难&#xff…

生产者消费者模型的同步与互斥:C++代码实现

文章目录 一、引言二、生产者消费者模型概述1、基本概念和核心思想2、生产者消费者模型的优点 三、消费者和生产者之间的同步与互斥四、代码实现1、事前准备2、环形队列的实现3、阻塞队列的实现4、两种实现方式的区别 一、引言 在现代计算机系统中&#xff0c;很多任务需要同时…

【机器学习】机器学习与教育科技在个性化教学中的融合应用与性能优化新探索

文章目录 引言机器学习与教育科技的基本概念机器学习概述监督学习无监督学习强化学习 教育科技概述学生学习行为分析个性化学习路径推荐智能化教育评估 机器学习与教育科技的融合应用实时学习数据分析数据预处理特征工程 学生成绩预测与优化模型训练模型评估 个性化学习路径推荐…

扩展学习|风险沟通研究进展综述

一、风险沟通研究进展综述&#xff08;2011年发表&#xff09; 文献来源&#xff1a;[1]高旭,张圣柱,杨国梁,等.风险沟通研究进展综述[J].中国安全生产科学技术, 2011, 7(5):5.DOI:10.3969/j.issn.1673-193X.2011.05.029. 简介&#xff1a;首先简述了风险沟通研究的社会背景按…

阿里云主机使用 docker-compose 部署 harbor 镜像仓库

文章目录 [toc]docker-compose 安装harbor 安装修改配置文件导入离线镜像部署前预处理开始安装开通安全策略组docker 配置 现在国内 dockerhub 镜像加速多多少少都有问题&#xff0c;这里采用离线包的方式来部署 harbor-github 下载地址 找到对应的版本&#xff0c;下载带有 of…

【AI实践】Ollama本地安装大模型服务

Ollama安装运行 安装与配置 Download Ollama 安装默认在C盘&#xff0c;成功后&#xff0c;window任务栏图标会有Ollama Logo 为了不占用C盘更大的空间&#xff0c;修改模型下载路径&#xff0c;修改环境变量 下载模型 由于我电脑是第六代Intel&#xff0c;集显&#xff0c;…

(56)MOS管专题--->(11)MOS管三个极的判定

(11)MOS管三个极的判定 1 目录 (a)IC简介 (b)数字IC设计流程 (c)Verilog简介 (d)MOS管三个极的判定 (e)结束 1 IC简介 (a)在IC设计中,设计师使用电路设计工具(如EDA软件)来设计和模拟各种电路,例如逻辑电路、模拟电路、数字信号处理电路等。然后,根…

【算法题】搜索二维矩阵,一文彻底弄会!

目录 一、题目描述 二、解题思路 1、引言 2、思路推导过程 三、参考答案 一、题目描述 搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数…