Spring Security入门教程,springboot整合Spring Security

news2024/11/27 19:48:01

Spring Security是Spring官方推荐的认证、授权框架,功能相比Apache Shiro功能更丰富也更强大,但是使用起来更麻烦。

如果使用过Apache Shiro,学习Spring Security会比较简单一点,两种框架有很多相似的地方。

目录

一、准备工作

创建springboot项目

pom.xml

application.yml

二、创建相关的类

UserDetailsService

SecurityConfig.java

SystemProperties.java

MybatisPlusConfig.java

三、完成登录接口

创建数据库实体类

创建持久层接口

创建登录DTO对象

创建控制器类

创建业务层类

自定义登录成功处理器


一、准备工作

创建springboot项目

首先,通过IntelliJ IDEA创建一个springboot项目,项目名为springboot-springsecurity,在pom.xml中添加相关依赖。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath />
    </parent>

    <groupId>com.example</groupId>
    <artifactId>springboot-springsecurity</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <jjwt.version>0.9.1</jjwt.version>
        <mysql.version>8.0.28</mysql.version>
        <druid.version>1.1.21</druid.version>
        <lombok.version>1.18.22</lombok.version>
        <mybatis.version>2.2.2</mybatis.version>
        <mybatis-plus.version>3.5.1</mybatis-plus.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--validation-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!--spring security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

        <!--jjwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

server:
  port: 8080
  servlet:
    context-path: /

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/spring_security
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

mybatis-plus:
  mapper-locations: classpath:mapper/*Mapper.xml

logging:
  level:
    springfox: error
    com.example.security: debug


system:
  login-page: /login.html
  login-url: /user/login
  index-page: /index.html
  logout-url: /user/logout
  parameter:
    username: username
    password: password
  white-url:
    - /js/**
    - /css/**
    - /images/**
    - /user/login
    - /login.html

二、创建相关的类

UserDetailsService

UserDetailsService接口是Spring Security中非常重要的接口,在登录认证的时候会通过这个接口的loadUserByUsername()方法获取用户的信息,来完成登录的用户名、密码校验,完成登录流程。

我们需要创建一个UserDetailsService的实现类,并声明为Spring组件。

package com.example.security.security;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.security.entity.User;
import com.example.security.exception.GlobalException;
import com.example.security.mapper.UserMapper;
import com.example.security.restful.ResponseCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
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.ArrayList;
import java.util.List;

/**
 * @author heyunlin
 * @version 1.0
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserMapper userMapper;

    @Autowired
    public UserDetailsServiceImpl(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户信息
        User user = selectByUsername(username);

        if (user == null) {
            throw new BadCredentialsException("登录失败,用户名不存在!");
        } else {
            List<String> permissions = selectPermissions(username);

            return org.springframework.security.core.userdetails.User.builder()
                    .username(user.getUsername())
                    .password(user.getPassword())
                    .accountExpired(false)
                    .accountLocked(false)
                    .disabled(!user.getEnable())
                    .credentialsExpired(false)
                    .authorities(permissions.toArray(new String[] {}))
                    .build();
        }
    }

    /**
     * 通过用户名查询用户信息
     * @param username 用户名
     * @return User
     */
    private User selectByUsername(String username) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();

        wrapper.eq("username", username);

        List<User> list = userMapper.selectList(wrapper);

        if (list.size() == 1) {
            return list.get(0);
        }

        return null;
    }

    /**
     * 通过用户名查询用户权限
     * @param username 用户名
     * @return List<String>
     */
    private List<String> selectPermissions(String username) {
        if (username == null) {
            throw new GlobalException(ResponseCode.BAD_REQUEST, "用户名不能为空");
        }

        List<String> permissions = new ArrayList<>();

        permissions.add("/user/login");
        permissions.add("/user/logout");
        permissions.add("/user/selectById");

        return permissions;
    }

}

SecurityConfig.java

创建security的配置类

package com.example.security.config;

import com.example.security.security.LoginFailHandler;
import com.example.security.security.LoginSuccessHandler;
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.crypto.password.PasswordEncoder;

/**
 * @author heyunlin
 * @version 1.0
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final SystemProperties systemProperties;

    @Autowired
    public SecurityConfig(SystemProperties systemProperties) {
        this.systemProperties = systemProperties;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {

            @Override
            public String encode(CharSequence charSequence) {
                return (String) charSequence;
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return charSequence.equals(s);
            }
        };
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用防跨域攻击
        http.csrf().disable();

        // 配置各请求路径的认证与授权
        http.formLogin()
                .loginPage(systemProperties.getLoginPage()) // 自定义登录页面的地址
                .loginProcessingUrl(systemProperties.getLoginUrl()) // 处理登录的接口地址
                .usernameParameter(systemProperties.getParameter().get("username")) // 用户名的参数名
                .passwordParameter(systemProperties.getParameter().get("password")) // 密码的参数名
                .successHandler(new LoginSuccessHandler(systemProperties))
                //.successForwardUrl("/index.html") // 登录成功跳转的地址
                .failureHandler(new LoginFailHandler()); // 登录失败的处理器

        // 退出登录相关配置
        http.logout()
                .logoutUrl(systemProperties.getLogoutUrl()) // 退出登录的接口地址
                .logoutSuccessUrl(systemProperties.getLoginUrl()); // 退出登录成功跳转的地址

        // 配置认证规则
        String[] toArray = systemProperties.getWhiteUrl().toArray(new String[]{});
        http.authorizeRequests()
                .antMatchers(toArray).permitAll() // 白名单,也就是不需要登录也能访问的资源
                .anyRequest().authenticated();
    }

}

SystemProperties.java

package com.example.security.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * @author heyunlin
 * @version 1.0
 */
@Data
@Component
@ConfigurationProperties(prefix = "system")
public class SystemProperties {

    /**
     * 登录页面
     */
    private String loginPage;

    /**
     * 登录的请求地址
     */
    private String loginUrl;

    /**
     * 登录成功后跳转的页面
     */
    private String indexPage;

    /**
     * 退出登录的请求地址
     */
    private String logoutUrl;

    /**
     * 白名单
     */
    private List<String> whiteUrl;

    /**
     * 登录的参数
     */
    private Map<String, String> parameter;
}

MybatisPlusConfig.java

package com.example.security.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author heyunlin
 * @version 1.0
 */
@Configuration
@MapperScan(basePackages = "com.example.security.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        return interceptor;
    }

}

三、完成登录接口

创建数据库实体类

User.java

package com.example.security.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 用户
 * @author heyunlin
 * @version 1.0
 */
@Data
@TableName("user")
public class User implements Serializable {
	private static final long serialVersionUID = 18L;

	@TableId(value = "id", type = IdType.INPUT)
	private String id;

	/**
	 * 姓名
	 */
	private String name;

	/**
	 * 性别
	 */
	private Integer gender;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 密码
	 */
	private String password;

	/**
	 * 手机号
	 */
	private String phone;

	/**
	 * 是否启用
	 */
	private Boolean enable;

	/**
	 * 最后一次登录时间
	 */
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
	private LocalDateTime lastLoginTime;
}

创建持久层接口

package com.example.security.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.security.entity.User;
import org.springframework.stereotype.Repository;

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper extends BaseMapper<User> {

}

创建登录DTO对象

package com.example.security.dto;

import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @author heyunlin
 * @version 1.0
 */
@Data
public class UserLoginDTO implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 用户名
     */
    @NotNull(message = "用户名不允许为空")
    @NotEmpty(message = "用户名不允许为空")
    private String username;

    /**
     * 密码
     */
    @NotNull(message = "密码不允许为空")
    @NotEmpty(message = "密码不允许为空")
    private String password;
}

创建控制器类

package com.example.security.controller;

import com.example.security.dto.UserLoginDTO;
import com.example.security.entity.User;
import com.example.security.restful.JsonResult;
import com.example.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author heyunlin
 * @version 1.0
 */
@RestController
@RequestMapping(path = "/user", produces = "application/json;charset=utf-8")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public JsonResult<Void> login(@Validated UserLoginDTO userLoginDTO) {
        userService.login(userLoginDTO);

        return JsonResult.success("登录成功");
    }

    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    public JsonResult<Void> logout() {
        userService.logout();

        return JsonResult.success("登出成功");
    }

    @RequestMapping(value = "/selectById", method = RequestMethod.GET)
    public JsonResult<User> selectById(@RequestParam(value = "id", required = true) String userId) {
        User user = userService.selectById(userId);

        return JsonResult.success(null, user);
    }

}

创建业务层类

UserService接口

package com.example.security.service;

import com.example.security.dto.UserLoginDTO;
import com.example.security.entity.User;

/**
 * @author heyunlin
 * @version 1.0
 */
public interface UserService {

    /**
     * 登录认证
     * @param userLoginDTO 用户登录信息
     */
    void login(UserLoginDTO userLoginDTO);

    /**
     * 退出登录
     */
    void logout();

    /**
     * 通过ID查询用户信息
     * @param userId 用户ID
     * @return User 通过ID查询到的用户信息
     */
    User selectById(String userId);
}

UserServiceImpl.java

package com.example.security.service.impl;

import com.example.security.dto.UserLoginDTO;
import com.example.security.entity.User;
import com.example.security.mapper.UserMapper;
import com.example.security.service.UserService;
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;

/**
 * @author heyunlin
 * @version 1.0
 */
@Service
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final AuthenticationManager authenticationManager;

    @Autowired
    public UserServiceImpl(UserMapper userMapper, AuthenticationManager authenticationManager) {
        this.userMapper = userMapper;
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void login(UserLoginDTO userLoginDTO) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                userLoginDTO.getUsername(),
                userLoginDTO.getPassword()
        );

        authenticationManager.authenticate(authentication);
    }

    @Override
    public void logout() {
        // todo
    }

    @Override
    public User selectById(String userId) {
        return userMapper.selectById(userId);
    }

}

自定义登录成功处理器

登陆成功直接重定向到/index.html

package com.example.security.security;

import com.example.security.config.SystemProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

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

/**
 * @author heyunlin
 * @version 1.0
 */
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    private final SystemProperties systemProperties;

    public LoginSuccessHandler(SystemProperties systemProperties) {
        this.systemProperties = systemProperties;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        response.sendRedirect(systemProperties.getIndexPage());
    }

}

至此,springboot整合Spring Security就完成了,项目结构如下。

文章就分享到这里了,代码已开源,可按需获取~

springboot整合spring securityicon-default.png?t=N7T8https://gitee.com/he-yunlin/springboot-springsecurity.git

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

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

相关文章

“Java与Redis的默契舞曲:优雅地连接与存储数据“

文章目录 引言1. Java连接上Redis2. Java对Redis进行存储数据2.1 存储set类型数据2.2 存储hash类型数据2.3 存储list类型数据 总结 引言 在现代软件开发中&#xff0c;数据存储和处理是至关重要的一环。Java作为一门强大的编程语言&#xff0c;与Redis这个高性能的内存数据库相…

【PWN · heap | unlink】hitcon2014_stkof

初学&#xff0c;通过一道题初步掌握unlink。不教学unlink的具体过程&#xff0c;仅是一篇wp记录笔记 前言 教学和具体过程可以看这个大佬的博客&#xff1a; buuctf pwn hitcon2014_stkof 初识unlink_buuctf hitcon2014_stkof-CSDN博客 一、题目 fill函数可读大量字符&#…

正点原子嵌入式linux驱动开发——Linux WIFI驱动

WIFI的使用已经很常见了&#xff0c;手机、平板、汽车等等&#xff0c;虽然可以使用有线网络&#xff0c;但是有时候很多设备存在布线困难的情况&#xff0c;此时WIFI就是一个不错的选择。正点原子STM32MP1开发板支持USB和SDIO这两种接口的WIFI&#xff0c;本章就来学习一下如何…

springboot前后端时间类型传输

springboot前后端时间类型传输 前言1.java使用时间类型java.util.Date2.java使用localDateTime 前言 springboot前后端分离项目总是需要进行时间数据类型的接受和转换,针对打代码过程中不同的类型转化做个总结 1.java使用时间类型java.util.Date springboot的项目中使用了new …

基于前馈神经网络完成鸢尾花分类

目录 1 小批量梯度下降法 1.0 展开聊一聊~ 1.1 数据分组 1.2 用DataLoader进行封装 1.3 模型构建 1.4 完善Runner类 1.5 模型训练 1.6 模型评价 1.7 模型预测 思考 总结 参考文献 首先基础知识铺垫~ 继续使用第三章中的鸢尾花分类任务&#xff0c;将Softm…

OCS2工具箱

实时系统优化控制工具箱 参考视频&#xff1a;ETH 最优控制/MPC 实时求解器 OCS2 使用入门 参考文档&#xff1a;OCS2 求解器入门 选择OCS2 OCS2 是一个 MPC 实时求解器 (SLQ/iLQR)&#xff0c;依赖 Pinocchio 构建机器人动力学模型&#xff0c;采用 RViz 或者 RaiSim 验证 (…

Ansible 自动化运维工具 --- playbook 剧本

文章目录 1. Host inventory ---- 主机清单1.1 简介1.2 inventory文件1.3 Inventory 文件的构成1.3.1 主机与组1.3.2 变量 1.4 inventory 中的常用变量 2. Ansible-playbook剧本2.1 简介2.2 Playbook的结构组成2.3 编写playbook的基本格式与写法2.3.1 基本格式2.3.2 语句的横向…

C语言打印出九九乘法表

#include<stdio.h> int main() {int i,j;for(i1;i<9;i){for(j1;j<9;j){printf("%d*%d%d\t",i,j,i*j); //\t制表符}printf("\n"); //\n输出个回车} }

Redis03-过期策略和淘汰策略

目录 Redis数据过期策略 Redis数据淘汰策略 Redis数据过期策略 Redis使用一种基于过期策略来处理键的过期和自动失效。这种策略可以确保不再需要的数据被自动删除&#xff0c;以释放内存并避免数据过期后仍然在缓存中存留。 Redis的过期删除策略主要有两种&#xff1a; 惰性…

【MySQL数据库】 四

本文主要介绍了mysql数据库的几种常见的约束. 一.数据库约束 我们希望存储的数据是靠谱的,mysql提供一些机制来辅助我们自动的依赖程序对数据进行检查 . 这类查数据的机制,就是约束 一旦约束好了,后续在进行增 删 改的时候,mysql就会自动的对修改的数据做出检查,如果不符合…

并发编程: 2. 线程管控

给定一个线程&#xff0c;只要令std::thread对象与之关联&#xff0c;就能管控该线程的几乎每个细节。 2.1 线程的基本管控 2.1.1 发起线程 线程通过构建std::thread对象而启动&#xff0c;该对象指明线程要运行的任务&#xff08;函数&#xff09;。简单的任务&#xff0c;…

【大数据】NiFi 中的重要术语

NiFi 中的重要术语 1.Flow Controller2.Processor3.Connection4.Controller Service5.Process Group6.FlowFile 那些一个个黑匣子称为 Processor&#xff0c;它们通过称为 Connection 的队列交换名为 FlowFile 的信息块。最后&#xff0c;FlowFile Controller 负责管理这些组件…

隐私安全|隐私安全已从国家法律法规转向商业企业应用,如何理解以及落地建设,相信大家正在经历隐私安全的困扰

网络空间的隐私安全主要是指网络隐私权不受侵犯&#xff0c;网络隐私权是指自然人在网上享有的&#xff0c;与公共利益无关的个人活动领域与个人信息秘密依法受到保护&#xff0c;不被他人非法侵扰&#xff0c;知悉收集&#xff0c;利用和公开的一种人格权&#xff0c;也包括第…

基于鹰栖息算法的无人机航迹规划-附代码

基于鹰栖息算法的无人机航迹规划 文章目录 基于鹰栖息算法的无人机航迹规划1.鹰栖息搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用鹰栖息算法来优化无人机航迹规划。 1.鹰栖息…

ActiveMq学习⑧__ActiveMQ的消息持久化机制

ActiveMQ的消息存储和持久化 MQ的高可用 事务持久签收可持久化 &#xff08;类似于与mq消息的同步机制&#xff09; 为了避免意外宕机以后丢失信息&#xff0c;需要做到重启后可以恢复消息队列&#xff0c;消息系统一半都会采用持久化机制。 ActiveMQ的消息持久化机制 Act…

canvas实现刮奖功能

canvas刮奖原理很简单&#xff0c;就是在刮奖区添加两个canvas&#xff0c;第一个canvas用于显示刮开后显示的内容&#xff0c;可以是一张图片或一个字符串&#xff0c;第二个canvas用于显示涂层&#xff0c;可以用一张图片或用纯色填充&#xff0c;第二个canvas覆盖在第一个ca…

逆向学习记录(5)刷机

首要前提&#xff1a;手机刷机必须OEM解锁&#xff08;BL解锁&#xff09;&#xff0c;否则不能刷机&#xff01; 1、根据手机机型下载手机系统包&#xff0c;首次登陆网址需要同意协议。然后在右侧选机型&#xff0c;中间就出来各种适合本机型的系统。 下载网址 https://dev…

47基于matlab的水印提取,将水印和载体进行图像融合

基于matlab的水印提取&#xff0c;将水印和载体进行图像融合&#xff0c;成为一体&#xff0c;可对合成图像进行加噪处理&#xff0c;剪切处理&#xff0c;小波压缩处理&#xff0c;旋转处理等操作&#xff0c;最后对合成图像实现水印提取&#xff0c;程序已调通&#xff0c;可…

【AI好好玩02】利用Lama Cleaner本地实现AIGC试玩:擦除对象、替换对象、更换风格等等

目录 一、安装二、擦除功能1. LaMa模型实操实例一&#xff1a;去除路人实操实例二&#xff1a;去水印实操实例三&#xff1a;老照片修复 2. LDM模型3. ZITS模型4. MAT模型5. FcF模型6. Manga模型 三、替换对象功能1. sd1.52. sd23. anything44. realisticVision1.45. 四个模型的…

Tomcat安装配置教程

目录 1、安装tomcat1.1、查看JDK版本1.2、 匹配对应的JDK版本1.3、 下载Tomcat1.3.1、 安装包版&#xff08;推荐&#xff0c;不用配环境&#xff09;1.3.2、 压缩包版 2、 运行Tomcat3、 不能运行问题 1、安装tomcat 1.1、查看JDK版本 由于不同版本tomcat对于jdk的版本有要求…