实现注册与登录(企业级)

news2024/11/15 15:40:33

目录

实现注册超级管理员功能(持久层)

一、判定系统是否已经绑定超级管理员

二、编写保存用户记录的代码

三、编写查询用户ID的代码

实现注册超级管理员功能(业务层)

一、获取OpenId

二、编写注册新用户的业务代码

掌握 RBAC 权限模型

一、RBAC权限模型

二、前后端权限验证

三、如何查询用户的权限列表?

实现注册超级管理员功能(Web层) 

一、创建表单类

二、创建Controller类 

定义全局路径和封装Ajax(移动端)

一、封装全局路径

二、封装Ajax

完成注册超级管理员功能(移动端)

实现用户登录功能(持久层&业务层)

一、如何判定登陆

二、编写持久层代码

三、编写业务层代码

实现用户登录功能(Web层) 

一、创建表单类

二、创建登陆Web方法

实现用户登录功能(移动端)

观察Emos后端项目运行细节

一、为什么XSSFilter最先执行?

二、OAuth2Filter的执行


实现注册超级管理员功能(持久层)

        mybatis-generate工具多表操作,生成的pojo和dao文件中 字段可能和数据表中的,不是一一对应。重新单表生成一次即可。

一、判定系统是否已经绑定超级管理员

        Emos系统中只可以绑定唯一的超级管理员账号,所以用户输入了 000000 这个激活码的时候,后端Java项目必须要判断是否可以绑定超级管理员。如果用户表中没有超级管理员记录,则可以绑定。否则就不能绑定超级管理员。 

        我们通过SQL语句就能查询出来用户表是否存在超级管理员账号,只需要查询 root字段 值为1的记录数量就可以了。 

在 TbUserDao.xml 文件中写入下面的SQL语句:

<select id="haveRootUser" resultType="boolean">
    SELECT IF(COUNT(*),TRUE,FALSE) FROM tb_user WHERE root=1;
</select>

在 TbUserDao.java 文件中创建DAO方法:

@Mapper
public interface TbUserDao { 
    public boolean haveRootUser();
}

二、编写保存用户记录的代码

        假设业务层判定用户可以注册成为超级管理员,于是我们要把用户的数据保存在用户表,这就需要我们编写相关的SQL语句和DAO代码。 

在 TbUserDao.xml 文件中写入下面的SQL语句:

<insert id="insert" parameterType="HashMap">
    INSERT INTO tb_user
    SET
    <if test="openId!=null">
        open_id = #{openId},
    </if>
    <if test="nickname!=null">
        nickname = #{nickname},
    </if>
    <if test="photo!=null">
        photo = #{photo},
    </if>
    <if test="name!=null">
        name = #{name},
    </if>
    <if test="sex!=null">
        sex = #{sex},
    </if>
    <if test="tel!=null">
        tel = #{tel},
    </if>
    <if test="email!=null">
        email=#{email},
    </if>
    <if test="hiredate!=null">
        hiredate = #{hiredate},
    </if>
    role = #{role},
    root = #{root},
    <if test="deptName!=null">
        dept_id = ( SELECT id FROM tb_dept WHERE dept_name = #{deptName} ),
    </if>
    status = #{status},
    create_time = #{createTime}
</insert>

在 TbUserDao.java 文件中创建DAO方法:

@Mapper
public interface TbUserDao { 
    ……
    public int insert(HashMap param);
}

三、编写查询用户ID的代码

        如果在员工表中插入新纪录,由于主键是自动生成的,所以我们并不知道新纪录的主键值是多少。于是我们要编写代码,根据OpenId查询用户ID 

在 TbUserDao.xml 文件中写入下面的SQL语句:

<select id="searchIdByOpenId" parameterType="String" resultType="Integer"> 
    SELECT id FROM tb_user WHERE open_id=#{openId} AND status = 1
</select>

在 TbUserDao.java 文件中创建DAO方法:

@Mapper
public interface UserDao { 
    ……
    public Integer searchIdByOpenId(String openId);
}

实现注册超级管理员功能(业务层)

        既然要写业务层的代码,肯定要先声明一个接口,然后再去定义实现类。为什么?业务层的代码经常随着需求的变化而发生变化。如果用户提出来一个新的需求, 那么咱们不是在原有业务层实现类上改代码,而是从接口里面再派生出一个子类,在新的子类里面去定义新的需求对应的代码。比如说电商网站上订单结算业务模块,普通日子是一个结算规则,促销节又是一个结算规则。如果在已有的业务模块实现类上,去添加“促销节”的代码,“促销节”过了后,代码还要再改回去,非常麻烦。从一个复杂的Java类里剥离复杂的业务代码,难度非常大。如果我们从接口中派生出一个新的实现类,在新的实现类中定义“促销节”规则,“促销节”过去后,系统使用原有的类。

        

        在MyBatis中,我们只需要定义接口,实现类由MyBatis框架通过动态代理来实现。

在 application.yml 添加微信小程序信息:

wx:
  app-id: xxxxxx
  app-secret: xxxxxx

RuntimeException: 获取OpenId时,微信方面的异常。

EmosException: 自己项目的异常。

        上一小节,我们封装了注册用户的持久层代码,下面就应该编写业务层的代码可。比如保存用户记录之前,我们要获得OpenId才行。

一、获取OpenId

        获取微信用户的 OpenId ,需要后端程序向微信平台发出请求,并上传若干参数,最终才能得到。URL请求路径:https://api.weixin.qq.com/sns/jscode2session 。

在 com.example.emos.wx.service 中创建 UserService.java 接口 

public interface UserService {

}

在 com.example.emos.wx.service.impl 中创建 UserServiceImpl.java 类

package com.example.emos.wx.service.impl;
……

@Service
@Slf4j
@Scope("prototype") 
public class UserServiceImpl implements UserService { 

    @Value("${wx.app-id}") 
    private String appId; 

    @Value("${wx.app-secret}")
    private String appSecret;

    @Autowired
    private TbUserDao userDao;

    private String getOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        HashMap map = new HashMap();
        map.put("appid", appId);
        map.put("secret", appSecret);
        map.put("js_code", code);
        map.put("grant_type", "authorization_code");
        String response = HttpUtil.post(url, map);
        JSONObject json = JSONUtil.parseObj(response);
        String openId = json.getStr("openid");
        if (openId == null || openId.length() == 0) {
            throw new RuntimeException("临时登陆凭证错误");
        }
        return openId;
    }
}

二、编写注册新用户的业务代码

在 UserService 接口中添加抽象方法的声明 

    public int registerUser(String registerCode,String code,String nickname,String photo);

在 UserServiceImpl 类中实现抽象方法

@Override
public int registerUser(String registerCode, String code, String nickname, String photo) {
    //如果邀请码是000000,代表是超级管理员
    if (registerCode.equals("000000")) {
        //查询超级管理员帐户是否已经绑定
        boolean bool = userDao.haveRootUser();
        if (!bool) {
            //把当前用户绑定到ROOT帐户
            String openId = getOpenId(code);
            HashMap param = new HashMap();
            param.put("openId", openId);
            param.put("nickname", nickname);
            param.put("photo", photo);
            param.put("role", "[0]");
            param.put("status", 1);
            param.put("createTime", new Date());
            param.put("root", true);
            userDao.insert(param);
            int id = userDao.searchIdByOpenId(openId);
            return id;
        } else {
            //如果root已经绑定了,就抛出异常
            throw new EmosException("无法绑定超级管理员账号");
        }
    }
    //TODO 此处还有其他判断内容
    else{
        return 0;
    }
}

 掌握 RBAC 权限模型

        MySQL5.7之后引入JSON数据类型。JSON数据类型可以保存两种数据,一:JSON对象,二:数组。

SELECT JSON_ARRAY(10, 20, 30)  # 创建JSON数组

SELECT JSON_CONTAINS(JSON_ARRAY(10, 20, 30), "100")  # JSON数组是否包含某元素

DISTINCT 平替 Set<String>

        这个小节我们不先不急着写Web层的代码,因为注册成功之后,我们要向客户端返回令牌之外,还要返回用户的权限列表,以后客户端就可以根据权限列表判定用户能看到什么页面内容,以及可以执行什么操作,所以这个小节我们先来学习一下RBAC权限模型。

一、RBAC权限模型

        RBAC的基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。这样做的好处是,不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理,减少系统的开销。 

        RBAC模型中的权限是由模块和行为合并在一起而产生的,在MySQL中,有 模块表(tb_module) 和 行为表(tb_action) ,这两张表的记录合并在一起就行程了权限记录,保存在 权限表(tb_permission) 中。

 

        现在知道了权限记录是怎么来的,下面我们看看怎么把权限关联到角色中。传统一点的做法是创建一个交叉表,记录角色拥有什么权限。但是现在 MySQL5.7 之后引入了 JSON 数据类型,所以我在 角色表(tb_role) 中设置的permissions字段,类型是JSON格式的。

 

        到目前为止,JSON类型已经支持索引机制,所以我们不用担心存放在JSON字段中的数据检索速度慢了。MySQL为JSON类型配备了很多函数,我们可以很方便的读写JSON字段中的数据。 

        接下来我们看看角色是怎么关联到用户的,其实我在 用户表(tb_user) 上面设置role字段,类型依旧是JSON的。这样我就可以把多个角色关联到某个用户身上了。

 

二、前后端权限验证

        关于权限验证的工作,前端要做,后端也要做。后端的权限验证还好说,Shiro框架可以做这个事情。但是移动端没有权限验证框架,所以需要我们自己封装函数来验证权限。每个页面在渲染的时候,先判断用户拥有什么权限,然后根据权限控制渲染的内容。比如说普通员工没有添加新员工的权限,所以界面上就不能出现添加按钮。 

        移动端做权限判断的前提是必须有当前用户的 权限列表 ,这个权限列表是用户 登陆成功 或者 注册成功 ,后端Java项目返回给移动端的,移动端保存到本地 Storage 里面。

三、如何查询用户的权限列表?

SELECT DISTINCT p.permission_name
FROM tb_user u
JOIN tb_role r ON JSON_CONTAINS(u.role, CAST(r.id AS CHAR))
JOIN tb_permission p ON JSON_CONTAINS(r.permissions, CAST(p.id AS CHAR))
WHERE u.id = 用户ID AND u.status = 1;

在 TbUserDao.xml 文件中添加上面的SQL语句,用来查询用户的权限列表

<select id="searchUserPermissions" parameterType="int" resultType="String">
    SELECT DISTINCT p.permission_name
    FROM tb_user u
    JOIN tb_role r ON JSON_CONTAINS(u.role, CAST(r.id AS CHAR))
    JOIN tb_permission p ON JSON_CONTAINS(r.permissions, CAST(p.id AS CHAR))
    WHERE u.id = #{userId} AND u.status = 1;
</select>

在 TbUserDao.java 接口中声明 searchUserPermissions() 方法 

public Set<String> searchUserPermissions(int userId);

在 UserService.java 接口中声明 searchUserPermissions() 方法 

public Set<String> searchUserPermissions(int userId);

在 UserServiceImpl.java 接口中实现 searchUserPermissions() 方法 

@Override
public Set<String> searchUserPermissions(int userId) { 
    Set<String> permissions=userDao.searchUserPermissions(userId);
    return permissions; 
}

实现注册超级管理员功能(Web层) 

一、创建表单类

接收移动端提交的注册请求,我们需要用表单类来封装数据,所以创建 RegisterForm.java 类。 

package com.example.emos.wx.controller.form;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

@Data
@ApiModel
public class RegisterForm {
    @NotBlank(message = "注册码不能为空")
    @Pattern(regexp = "^[0-9]{6}$",message = "注册码必须是6位数字")
    private String registerCode;

    @NotBlank(message = "微信临时授权不能为空")
    private String code;
    @NotBlank(message = "昵称不能为空")
    private String nickname;

    @NotBlank(message = "头像不能为空")
    private String photo;
}

二、创建Controller类 

处理移动端提交的请求,我们需要Controller类,所以创建 UserController.java 类。

问:业务层采用先定义接口,后声明实现类的做法,为什么Web层不这么做?

答:业务层的需求经常变化,所以应该先声明接口,然后再写实现类。Web层这里变化并不大,可以直接定义具体类。

package com.example.emos.wx.controller;
……

@RestController
@RequestMapping("/user")
@Api("用户模块Web接口")
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${emos.jwt.cache-expire}")
    private int cacheExpire;

    @PostMapping("/register")
    @ApiOperation("注册用户")
    public R register(@Valid @RequestBody RegisterForm form) {
        int id = userService.registerUser(form.getRegisterCode(), form.getCode(),
form.getNickname(), form.getPhoto());
        String token = jwtUtil.createToken(id);
        Set<String> permsSet = userService.searchUserPermissions(id);
        saveCacheToken(token, id);
        return R.ok("用户注册成功").put("token", token).put("permission", permsSet);
    }

    private void saveCacheToken(String token, int userId) {
        redisTemplate.opsForValue().set(token, userId + "", cacheExpire, TimeUnit.DAYS);
    }
}

 定义全局路径和封装Ajax(移动端)

一、封装全局路径

上节课我们创建好了后端的register方法,那么移动端发出请求,首先要填写好URL地址。为了在移动端项目上集中管理URL路径,我们可以在 main.js 文件中用全局变量的语法,定义全局的URL地址,这样更加便于维护。 

let baseUrl = "http://192.168.99.216:8080/emos-wx-api"
Vue.prototype.url = { 
    register: baseUrl + "/user/register", 
}

二、封装Ajax

移动端通过Ajax向服务端提交请求,然后接收到的响应分若干种情况: 

        1. 如果用户没有登陆系统,就跳转到登陆页面。 

        2. 如果用户权限不够,就显示提示信息。 

        3. 如果后端出现异常,就提示异常信息。 

        4. 如果后端验证令牌不正确,就提示信息。 

        5. 如果后端正常处理请求,还要判断响应中是否有Token。如果令牌刷新了,还要在本地存储Token。 

        如果移动端每次发出Ajax,都要做这么多的判断,我们的重复性劳动太多了。所以尽可能的把Ajax封装起来,减少重复性的劳动。

Vue.prototype.ajax = function(url, method, data, fun) {
    uni.request({
        "url": url,
        "method": method,
        "header": {
            token: uni.getStorageSync('token')
        },
        "data": data,
        success: function(resp) {
            if (resp.statusCode == 401) {
                uni.redirectTo({
                        url: '../login/login'
                });
            } else if (resp.statusCode == 200 && resp.data.code == 200) {
                let data = resp.data
                if (data.hasOwnProperty("token")) {
                    console.log(resp.data)
                    let token = data.token
                    uni.setStorageSync("token", token)
                }
                fun(resp)
            } else {
                uni.showToast({
                    icon: 'none',
                    title: resp.data
                });
            }
        }
    });
}

完成注册超级管理员功能(移动端)

        ......

实现用户登录功能(持久层&业务层)

        我们完成了超级管理员注册流程之后,用户表中就已经有了超级管理员记录,那么接下来我们可以利用这个用户记录来完成Emos小程序的微信登陆功能。

一、如何判定登陆

        用户表中并没有密码字段,我们无法根据username和password来判定用户是否可以登录。因为用户要拿着微信登陆Emos小程序,在用户表中只有 openid 、 nickname 和 photo 跟微信账号相关,我们应该如何判定用户登陆? 

        我们可以这样设计,用户在Emos登陆页面点击登陆按钮,然后小程序把 临时授权字符串 提交给后端Java系统。后端Java系统拿着临时授权字符串换取到 openid ,我们查询用户表中是否存在这个 openid 。如果存在,意味着该用户是已注册用户,可以登录。如果不存在,说明该用户尚未注册,目前还不是我们的员工,所以禁止登录。

二、编写持久层代码

在 TbUserDao.xml 文件中,编写查询语句 

<select id="searchIdByOpenId" parameterType="String" resultType="Integer"> 
    SELECT id FROM tb_user WHERE open_id=#{openId} AND status = 1
</select>

在 TbUserDao.java 中,定义DAO方法 

public Integer searchIdByOpenId(String openId);

三、编写业务层代码

在 UserService.java 中定义抽象方法 

public Integer login(String code);

在 UserServiceImpl.java 中实现抽象方法 

@Override
public Integer login(String code) { 
    String openId = getOpenId(code);
    Integer id = userDao.searchIdByOpenId(openId);
    if (id == null) { 
        throw new EmosException("帐户不存在");
    } 
    //TODO 从消息队列中接收消息,转移到消息表
    return id;
}

实现用户登录功能(Web层) 

有异常会抛出,不担心出现null,可以直接用 int id。

一、创建表单类

创建 LoginForm.java 类,封装客户端提交的数据。 

@ApiModel
@Data
public class LoginForm { 
    @NotBlank(message = "临时授权不能为空") 
    private String code; 
}

二、创建登陆Web方法

在 UserController.java 中创建 login() 方法。 

@PostMapping("/login") 
@ApiOperation("登陆系统") 
public R login(@Valid @RequestBody LoginForm form) { 
    int id = userService.login(form.getCode());
    String token = jwtUtil.createToken(id);
    Set<String> permsSet = userService.searchUserPermissions(id);
    saveCacheToken(token, id);
    return R.ok("登陆成功").put("token", token).put("permission", permsSet);
} 

判定用户登陆成功之后,向客户端返回权限列表和Token令牌。

实现用户登录功能(移动端)

        …………

观察Emos后端项目运行细节

        之前我们在SpringBoot项目中添加了很多第三方的技术,包括我们自己也写了很多有关的配置程序。其中包括 Servlet过滤器 , Shiro过滤器 ,以及 AOP拦截器 。Emos后端项目运行的时候,这些程序的执行顺序是什么?哪里是入口,我们还不太了解,所以这个小节,我们利用登陆案例来观察Emos后端项目的运行细节。

一、为什么XSSFilter最先执行?

Emos系统接收到 HTTP请求 之后,首先由 XSSFilter 来处理请求。因为 XSSFilter 是标准的Servlet过滤器 ,所以他执行的优先级要高于 ShiroFilter 和 AOP拦截器 的。这也很好理解,还没轮到Controller中的Web方法执行,AOP连接器自然不能运行。另外, XSSFilter 使用@WebFilter 注解定义出来的过滤器,所以他的优先级比 SpringMVC 中注册的 Filter 优先级更高,所以 XSSFilter 早于 SpringMVC 执行。这个也能说得通,我们希望先把请求中的数据先转义,然后再由SpringMVC框架来处理请求。

二、OAuth2Filter的执行

        因为OAuth2Filter是在SpringMVC中注册的Filter,所以它晚于Servlet过滤器的执行。但是SpringMVC中注册过滤器有个好处,就是可以规定Filter的优先级别,所以定义普通的Filter,注册在SpringMVC上更加的妥当。 

        我们在定义OAuth2Filter的时候,声明了很多的方法,但是在注册流程中,我们只能看到doFilterInternal()方法的执行,这又是为什么呢? 

@Override
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { 
    super.doFilterInternal(request, response, chain);
} 

        我们声明Shiro过滤器拦截路径的时候,为登陆和注册路径下的请求,设置了放行,所以验证与授权并没有生效。等我们将来写具体的业务类型的Web方法,添加相关的Shiro注解,这时候OAuth2Filter中的其他方法就得以运行了。

Map<String,String> filterMap=new LinkedHashMap<>();
filterMap.put("/webjars/**", "anon");
filterMap.put("/druid/**", "anon");
filterMap.put("/app/**", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/swagger/**", "anon");
filterMap.put("/v2/api-docs", "anon");
filterMap.put("/swagger-ui.html", "anon");
filterMap.put("/swagger-resources/**", "anon");
filterMap.put("/captcha.jpg", "anon");
filterMap.put("/user/register", "anon");
filterMap.put("/user/login", "anon");
filterMap.put("/test/**", "anon");
filterMap.put("/**", "oauth2");

三、TokenAspect的作用

        TokenAspect是切面类,拦截所有Web方法的返回值。TokenAspect先检测ThreadLocalToken中有没有令牌字符串?如果有就把刷新后的令牌写入Web方法返回的R对象里面。因此说,Web方法每次执行的时候,TokenAspect都会随之运行,这在正常不过了。

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

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

相关文章

tuple 和数组区别

元组&#xff08;tuple&#xff09;和数组&#xff08;array&#xff09;都是 Python 中用于存储多个值的数据结构&#xff0c;但它们在实现和使用上有一些区别。 元组是不可变的&#xff0c;而数组是可变的。即元组一旦创建&#xff0c;其内容就不能被修改&#xff0c;而数组…

高数笔记1(第一章函数 极限 连续 第一节函数第二节极限-极限的概念与性质)

目录 第一章 函数 极限 连续第一节 函数第二节 极限一、极限的概念与性质数列的极限例1例2 函数的极限极限的性质&#xff08;保号性重点 有界性&#xff09;例12例13例14 函数极限与数列极限的关系例15 第一章 函数 极限 连续 第一节 函数 判断有界要用函数的绝对值&#xff…

Toolformer:可以教会自己使用工具的语言模型

Toolformer&#xff1a;可以教会自己使用工具的语言模型 摘要Introduction现有大模型的局限处理办法本文的idea Approach样例化API调用执行API调用筛选API调用模型微调 实验局限 论文地址点这里 摘要 语言模型&#xff08;LMs&#xff09;呈现了令人深刻的仅使用少量的范例或…

2022(一等奖)D1649基于多源卫星遥感的干旱区农作物耗水精细模拟

作品介绍 1 研究背景及目标 1.1 研究区概况 本次研究的研究区位于甘肃省张掖市内。张掖市位于甘肃省西部&#xff0c;河西走廊中段&#xff0c;属干旱和半干旱两种气候类型&#xff0c;其特点是夏季短而酷热&#xff0c;冬季长而严寒&#xff0c;干旱少雨&#xff0c;且降水分…

MySQL-SQL存储过程/触发器详解(下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

ubuntu20.4服务器搭建ftp并连接(阿里云服务器)

首先在控制台添加ftp防火墙规则&#xff1a; 然后进入服务器安装ftp&#xff08;安装vsftpd&#xff09;: sudo apt update sudo apt install vsftpd使用以下命令检查其状态&#xff1a; sudo service vsftpd status如果FTP服务器未运行&#xff0c;请使用以下命令启动它&am…

强化学习:时序差分算法 TD-learning

例子引入 首先&#xff0c;我们考虑简单的平均估计计算&#xff1a; w E [ X ] wE[X] wE[X]&#xff0c;根据 RM算法 计算过程如下&#xff1a; 接着上面的例子&#xff0c;我们现在考虑一个较为复杂的问题&#xff0c;估计函数 v ( X ) v(X) v(X) 的平均值&#xff0c;根据 …

3.数据模型

文章目录 前言1.对象&#xff08;Object&#xff09;1.1 标准对象1.2 自定义对象1.2.1 创建一个property&#xff08;房产&#xff09;自定义对象1.2.2创建一个Favorite&#xff08;收藏夹&#xff09;自定义对象1.2.3 创建对象的注意事项 1.3 字段1.3.1 为property object创建…

迅镭激光参展CESC2023中国(江苏)国际储能大会,共话储能产业发展趋势!

2023年6月14日 中国(江苏)国际储能大会 暨智慧储能技术及应用展览会 在南京国际博览中心隆重开幕 迅镭激光携多款新能源解决方案 精彩亮相A区5C21展位 聚焦新能源电池绿色智能制造 共话储能行业创新发展之路 本次展会为期三天(6月14-16日) 诚邀广大行业同仁莅临参观交流! 本届大…

全志V3S嵌入式驱动开发(spi-nor驱动)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 所谓的spi nor驱动&#xff0c;其实就是让spi nor芯片&#xff0c;在linux kernel 5.2.y启动后可以被正确地识别到。前面有一篇文章&#xff0c;我…

POSIX信号量(基于生产消费模型)

目录 &#x1f34a;一、信号量 1.1之前代码的不足之处 1.2什么是信号量 &#x1f34a;二、信号量接口 &#x1f34a;三、信号量版本的生产消费模型 ①单生产单消费 ②多生产多消费 &#x1f34a;四、线程池 &#x1f34a;五、线程安全的单例模式 &#x1f34a;六、其他常…

大模型高效微调综述下: DiffPruning、BitFit、LoRa、AdaLoRA、MAM Adapters、UniPELT

文章目录 四、Selective Methods4.1 DiffPruning&#xff08;2020.10&#xff09;4.2 BitFit&#xff08;2021.6&#xff09;4.3 Freeze and Reconfigure (FAR&#xff0c;2022)4.4 FishMask&#xff08;略&#xff09; 五、Reparametrization-based methods&#xff08;重参数…

Selenium自动化工具集 - 完整指南和使用教程

文章目录 Selenium 的概述&#xff1a;Selenium 的安装与环境配置&#xff1a;Selenium WebDriver 的基本概念&#xff1a;定位元素的方法&#xff1a;常用操作方法&#xff1a;获取所有的 cookie&#xff1a;获取指定名称的 cookie&#xff1a;添加 cookie&#xff1a;删除指定…

JavaFX应用开发教程——基于JDK9与NetBeans实现

ISBN: 978-7-302-61499-9 作者&#xff1a;宋波 页数&#xff1a;257页 阅读时间&#xff1a;2023-06-18 推荐指数&#xff1a;★★★★★ 《JavaFX应用开发教程——基于JDK9与NetBeans实现》 是目前市面上讲解Java桌面开发为数不多的教材 &#xff08;JavaFX是Java语言的下一代…

Flutter系列(十二)实现购物车和提交订单页

基础工程&#xff1a; Flutter系列&#xff08;十一&#xff09;实现商城首页和商品详情页_摸金青年v的博客-CSDN博客 Flutter系列&#xff08;四&#xff09;底部导航顶部导航图文列表完整代码_摸金青年v的博客-CSDN博客 一、前言 本文用flutter实现购物车和提交订单页&#x…

【裸机开发】认识中断向量表(设置中断向量偏移的原因)

之前的LED驱动不存在中断&#xff0c;也就不包含中断的初始化。如果程序包含了中断&#xff0c;我们应还需要初始化哪些内容&#xff1f;要解决这个问题&#xff0c;我们需要先了解一个中断系统包含了哪些内容。 ① 中断向量表&#xff1a;描述中断对应的中断服务函数&#xf…

数据结构——栈的实现(动态增长版本)

堆栈&#xff08;stack&#xff09;又称为栈或堆叠&#xff0c;是计算机科学中的一种抽象资料类型&#xff0c;只允许在有序的线性资料集合的一端&#xff08;称为堆栈顶端&#xff0c;top&#xff09;进行加入数据&#xff08;push&#xff09;和移除数据&#xff08;pop&…

【软件架构】流水线设计模式

流水线模式 流水线模式是一种软件设计模式&#xff0c;它提供了构建和执行一系列操作的能力。 此模式最好与插件模式结合使用&#xff0c;以便在应用程序启动时动态构建流水线。 顺序 流水线的最基本实现是一个简单的操作序列。 public interface IOperation<T>{void …

【学习笔记】关于transformer

1.Embedding 一文读懂Embedding的概念&#xff0c;以及它和深度学习的关系 - 知乎 one-hot编码当矩阵过于稀疏时计算开销大&#xff0c;于是加上Embedding层&#xff0c;通过Embedding层&#xff08;矩阵乘法&#xff09;实现降维。 Embedding层将一个一个词&#xff08;词源…

Spring Boot 如何使用 Spring Security 进行认证和授权

Spring Boot 如何使用 Spring Security 进行认证和授权 在 Web 应用程序中&#xff0c;认证和授权是非常重要的功能。Spring Security 是一个基于 Spring 框架的强大的安全框架&#xff0c;它提供了完整的认证和授权解决方案&#xff0c;并且可以轻松地集成到 Spring Boot 应用…