SpringBoot+MyBatis用户注册实战

springboot实现最基础的登录注册功能(步骤详细,逻辑清晰) springboot实现最基础的登录注册功能 阅读详情
AI摘要

本文介绍了如何在Spring Boot项目中初始化项目,添加必要依赖,配置数据库连接,并实现用户注册、登录功能,包括实体类、Mapper接口、业务逻辑和服务层控制,以及前端页面的交互和后端拦截器的设置。

初始化项目

通过官网初始化SpringBoot项目,包结构如图所示:
在这里插入图片描述

添加pom依赖

<!--springboot启动类-->
 		<dependency>
         	<groupId>org.springframework.boot</groupId>
         	<artifactId>spring-boot-starter</artifactId>
        </dependency>
<!--webmvc-->
		<dependency>
        	<groupId>org.springframework.boot</groupId>
       		<artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!--devtools自动部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
<!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
<!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--Mybatis-->
      <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.1</version>
        </dependency>
<!--devtools自动部署-->
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

maven添加阿里云镜像仓库

<repositories>
        <repository>
            <id>nexus-aliyun</id>
            <name>nexus-aliyun</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

application.yml相关配置

server:
  port: 9090
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/DatabaseName?serverTimezone=GMT%2b8
mybatis:
  mapper-locations: classpath:mapper/*.xml #扫描所有mybatis的xml文件
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql

使用navicat工具连接MySQL数据库
在这里插入图片描述

一、实现用户注册功能

user
CREATE TABLE `user` (
  `user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `user_account` varchar(255) DEFAULT NULL COMMENT '用户账号',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '盐值',
  `user_name` varchar(255) DEFAULT NULL COMMENT '用户姓名',
  `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3;
entity
@Getter
@Setter
public class User {
        private Integer userId;
        private String userAccount;
        private String password;
        private String salt;
        private String userName;
        private String email;
}
mapper

public interface UserMapper {
    /**
     * 插入一条用户数据
     * @param user
     * @return 受影响的行数
     */
    Integer insert(User user);

    /**
     * 根据用户名查询用户数据
     * @param username
     * @return 如果找到对应的用户则返回这个用户的数据,没有找到返回null值
     */
    User findByUsername(String username);

    /**
     * 根据id删除一条用户
     * @param id
     * @return 返回受影响行数
     */
    Integer deleteById(Integer id);

    /**
     * 更新一条数据
     * @param user
     * @return 返回受影响的行数
     */
    Integer update(User user);

}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace属性指定当前映射文件和哪个接口进行映射,需要标注包的完整路径接口-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!--自定义映射规则-->
    <resultMap id="DifUser" type="com.example.demo.entity.User">
        <id column="user_id" property="userId"></id>
        <result column="user_account" property="userAccount"></result>
        <result column="user_name" property="userName"></result>
        <result column="email" property="email"></result>
    </resultMap>
    <!--id指定接口方法的名称-->
    <insert id="insert">
        INSERT INTO user(user_account,password,salt,user_name,email) values (#{userAccount},#{password},#{salt},#{userName},#{email})
    </insert>

<!--当表的字段和类的对象的属性不一致时,来自定义查询结果集的映射规则-->
    <select id="findByUsername" resultMap="DifUser">
        SELECT * FROM user WHERE user_name=#{userName}
    </select>

    <delete id="deleteById">
        DELETE FROM user WHERE user_id=#{userId}
    </delete>

    <update id="update">
        update user set user_account=#{userAccount},user_name=#{userName},email=#{email} where user_id=#{userId}
    </update>
</mapper>

IUserService:用户注册方法在这个接口中实现
public interface IUserService {
    /**
     * 用户注册方法
     * @param user 用户的数据对象
     */
    void reg(User user);

}

UserServiceImpl:实现用户注册功能相关逻辑

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public void reg(User user) {
        User result = userMapper.findByUsername(user.getUserName());
        //如果结果不为null,则抛出用户名被占用异常
        if (result!=null){
            throw new UsernameDuplicatedException("用户名被占用");
        }else {
            //如果结果为null,则执行注册业务
            String oldPassword=user.getPassword();
            String salt= UUID.randomUUID().toString().toUpperCase();
            user.setSalt(salt);
            String md5Password = getMd5Password(oldPassword, salt);
            System.out.println(md5Password);
            user.setPassword(md5Password);
            Integer rows = userMapper.insert(user);
            if (rows!=1){
                throw new InsertException("注册失败");
            }
        }
    }
    /**
     * md5算法加密
     */
    private String getMd5Password(String password,String salt){
        for (int i = 0; i < 3; i++) {
            //三次加密
            password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
        }
        //返回加密的密码
        return password;
    }
}
ServiceException :业务异常处理基类
public class ServiceException extends RuntimeException {
    public ServiceException() {
        super();
    }

    public ServiceException(String message) {
        super(message);
    }

    public ServiceException(String message, Throwable cause) {
        super(message, cause);
    }

    public ServiceException(Throwable cause) {
        super(cause);
    }

    protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

InsertException:插入方法异常

public class InsertException extends ServiceException {
    public InsertException() {
        super();
    }

    public InsertException(String message) {
        super(message);
    }

    public InsertException(String message, Throwable cause) {
        super(message, cause);
    }

    public InsertException(Throwable cause) {
        super(cause);
    }

    protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

UsernameDuplicatedException:用户名被占用异常
public class UsernameDuplicatedException extends ServiceException{
    public UsernameDuplicatedException() {
        super();
    }

    public UsernameDuplicatedException(String message) {
        super(message);
    }

    public UsernameDuplicatedException(String message, Throwable cause) {
        super(message, cause);
    }

    public UsernameDuplicatedException(Throwable cause) {
        super(cause);
    }

    protected UsernameDuplicatedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
JsonResult:响应结果类
@Data
public class JsonResult<T> implements Serializable {
    private Integer state;
    private String message;
    private T data;
}
UserController:用户方法统一接口

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserMapper userMapper;

    @Resource
    private IUserService userService;

    @GetMapping("/{username}")
    public Result findByUsername(@PathVariable String username){
        return Result.success(userMapper.findByUsername(username));
    }

    @GetMapping("/reg")
    public JsonResult<Void> reg(User user){
        JsonResult<Void> jsonResult = new JsonResult<>();
        try {
            userService.reg(user);
            jsonResult.setState(200);
            jsonResult.setMessage("注册成功");
        } catch (UsernameDuplicatedException e) {
             jsonResult.setState(4000);
             jsonResult.setMessage("用户名被占用");
        }catch (InsertException e){
            jsonResult.setState(5000);
            jsonResult.setMessage("注册时产生异常");
        }
        return jsonResult;
    }
}
UserMapperTest :测试类
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void insert(){
        User user = new User();
        user.setUserAccount("admin");
        user.setUserName("管理员");
        Integer rows = userMapper.insert(user);
        System.out.println(rows);
    }

    @Test
    public void deleteById(){
        Integer integer = userMapper.deleteById(7);
        System.out.println(integer);
    }

    @Test
    public void update(){
        User user = new User();
        user.setUserId(1);
        user.setUserName("admin");
        user.setUserAccount("管理员");
        user.setEmail("admin@qq.com");
        Integer update = userMapper.update(user);
        System.out.println(update);
    }
}

二、测试接口

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

用户注册

1 用户-创建数据表

1.使用use命令先选中store数据库。

USE store;

2.在store数据库中创建t_user用户数据表。

CREATE TABLE t_user (
	uid INT AUTO_INCREMENT COMMENT '用户id',
	username VARCHAR(20) NOT NULL UNIQUE COMMENT '用户名',
	password CHAR(32) NOT NULL COMMENT '密码',
	salt CHAR(36) COMMENT '盐值',
	phone VARCHAR(20) COMMENT '电话号码',
	email VARCHAR(30) COMMENT '电子邮箱',
	gender INT COMMENT '性别:0-女,1-男',
	avatar VARCHAR(50) COMMENT '头像',
	is_delete INT COMMENT '是否删除:0-未删除,1-已删除',
	created_user VARCHAR(20) COMMENT '日志-创建人',
	created_time DATETIME COMMENT '日志-创建时间',
	modified_user VARCHAR(20) COMMENT '日志-最后修改执行人',
	modified_time DATETIME COMMENT '日志-最后修改时间',
	PRIMARY KEY (uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2 用户-创建实体类

1.项目中许多实体类都会有日志相关的四个属性,所以在创建实体类之前,应先创建这些实体类的基类,将4个日志属性声明在基类中。在com.cy.store.entity包下创建BaseEntity类,作为实体类的基类。

package com.cy.store.entity;
import java.io.Serializable;
import java.util.Date;

/** 实体类的基类 */
public class BaseEntity implements Serializable {
    private String createdUser;
    private Date createdTime;
    private String modifiedUser;
    private Date modifiedTime;

    // Generate: Getter and Setter、toString()
}

因为这个基类的作用就是用于被其它实体类继承的,所以应声明为抽象类。

2.创建com.cy.store.entity.User用户数据的实体类,继承自BaseEntity类,在类中声明与数据表中对应的属性。

package com.cy.store.entity;
import java.io.Serializable;
import java.util.Objects;

/** 用户数据的实体类 */
public class User extends BaseEntity implements Serializable {
    private Integer uid;
    private String username;
    private String password;
    private String salt;
    private String phone;
    private String email;
    private Integer gender;
    private String avatar;
    private Integer isDelete;

	// Generate: Getter and Setter、Generate hashCode() and equals()、toString()
}

3 用户-注册-持久层

3.1 准备工作

1.在src/test/java下的com.cy.store.StoreApplicationTests测试类中编写并执行“获取数据库连接”的单元测试,以检查数据库连接的配置是否正确。

@Autowired
private DataSource dataSource;

@Test
public void getConnection() throws Exception {
	System.out.println(dataSource.getConnection());
}

2.执行src/test/java下的com.cy.toreApplicationTests测试类中的contextLoads()测试方法,以检查测试环境是否正常。

3.2 规划需要执行的SQL语句

1.用户注册的本质是向用户表中插入数据,需要执行的SQL语句大致是:

INSERT INTO t_user (除了uid以外的字段列表) VALUES (匹配的值列表)

2.由于数据表中用户名字段被设计为UNIQUE,在执行插入数据之前,还应该检查该用户名是否已经被注册,因此需要有“根据用户名查询用户数据”的功能。需要执行的SQL语句大致是:

SELECT * FROM t_user WHERE username=?
3.3 接口与抽象方法

1.创建com.cy.store.mapper.UserMapper接口,并在接口中添加抽象方法。

package com.cy.mapper;
import com.cy.store.entity.User;

/** 处理用户数据操作的持久层接口 */
public interface UserMapper {
    /**
     * 插入用户数据
     * @param user 用户数据
     * @return 受影响的行数
     */
    Integer insert(User user);

    /**
     * 根据用户名查询用户数据
     * @param username 用户名
     * @return 匹配的用户数据,如果没有匹配的数据,则返回null
     */
    User findByUsername(String username);
}

2.由于这是项目中第一次创建持久层接口,还应在StoreApplication启动类之前添加@MapperScan(“com.cy.store.mapper”)注解,以配置接口文件的位置。

MyBatis与Spring整合后需要实现实体和数据表的映射关系。实现实体和数据表的映射关系可以在Mapper接口上添加@Mapper注解。但建议以后直接在SpringBoot启动类中加@MapperScan(“mapper包”) 注解,这样会比较方便,不需要对每个Mapper都添加@Mapper注解。

@SpringBootApplication
@MapperScan("com.cy.store.mapper")
public class StoreApplication {

	public static void main(String[] args) {
		SpringApplication.run(StoreApplication.class, args);
	}
}
3.4 配置SQL映射

1.在src/main/resources下创建mapper文件夹,并在该文件夹下创建UserMapper.xml映射文件,进行以上两个抽象方法的映射配置。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper">
    <resultMap id="UserEntityMap" type="com.cy.store.entity.User">
        <id column="uid" property="uid"/>
        <result column="is_delete" property="isDelete"/>
        <result column="created_user" property="createdUser"/>
        <result column="created_time" property="createdTime"/>
        <result column="modified_user" property="modifiedUser"/>
        <result column="modified_time" property="modifiedTime"/>
    </resultMap>

    <!-- 插入用户数据:Integer insert(User user) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="uid">
        INSERT INTO
            t_user (username, password, salt, phone, email, gender, avatar, is_delete, created_user, created_time, modified_user, modified_time)
        VALUES
        (#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
    </insert>

    <!-- 根据用户名查询用户数据:User findByUsername(String username) -->
    <select id="findByUsername" resultMap="UserEntityMap">
        SELECT
            *
        FROM
            t_user
        WHERE
            username = #{username}
    </select>
</mapper>

2.由于这是项目中第一次使用SQL映射,所以需要在application.properties中添加mybatis.mapper-locations属性的配置,以指定XML文件的位置。

mybatis.mapper-locations=classpath:mapper/*.xml

3.完成后及时执行单元测试,检查以上开发的功能是否可正确运行。在src/test/java下创建com.cy.store.mapper.UserMapperTests单元测试类,在测试类的声明之前添加@RunWith(SpringRunner.class)和@SpringBootTest注解,并在测试类中声明持久层对象,通过自动装配来注入值。

@RunWith(SpringRunner.class)注解是一个测试启动器,可以加载SpringBoot测试注解。

package com.cy.store.mapper;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

// @RunWith(SpringRunner.class)注解是一个测试启动器,可以加载Springboot测试注解
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTests {
    @Autowired
    private UserMapper userMapper;

}

4.如果在第四步自动装配userMapper时,报“Could not autowire. No beans of ‘UserMapper’ type found”错,无法进行自动装配。解决方案是,将Autowiring for bean class选项下的Severity设置为Warning即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RsQY7MlL-1646190006945)(img/1.png)]

5.然后编写两个测试方法,对以上完成的两个功能进行单元测试。

单元测试方法必须为public修饰,方法的返回值类型必须为void,方法不能有参数列表,并且方法被@Test注解修饰。

@Test
public void insert() {
    User user = new User();
    user.setUsername("user01");
    user.setPassword("123456");
    Integer rows = userMapper.insert(user);
    System.out.println("rows=" + rows);
}

@Test
public void findByUsername() {
    String username = "user01";
    User result = userMapper.findByUsername(username);
    System.out.println(result);
}

如果出现org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)异常可能原因:

1.在resources文件加下创建的mapper文件夹类型没有正确选择(eclipse选择Folder,idea选择Directory)。

2.映射文件的mapper标签的namespace属性没有正确映射到dao层接口,或者application.properties中的属性mybatis.mapper-locations没有正确配置xml映射文件。

4 用户-注册-业务层

4.1 业务的定位

1.业务:一套完整的数据处理过程,通常表现为用户认为的一个功能,但是在开发时对应多项数据操作。在项目中,通过业务控制每个“功能”(例如注册、登录等)的处理流程和相关逻辑。

2.流程:先做什么,再做什么。例如:注册时,需要先判断用户名是否被占用,再决定是否完成注册。

3.逻辑:能干什么,不能干什么。例如:注册时,如果用户名被占用,则不允许注册;反之,则允许注册。

4.业务的主要作用是保障数据安全和数据的完整性、有效性。

4.2 规划异常

1.关于异常

1.请列举你认识的不少于十种异常:

Throwable
	Error
		OutOfMemoryError(OOM)
	Exception
		SQLException
		IOException
			FileNotFoundException
		RuntimeException
			NullPointerException
			ArithmeticException
			ClassCastException
			IndexOutOfBoundsException
				ArrayIndexOutOfBoundsException
				StringIndexOutOfBoundsException

2.异常的处理方式和处理原则:

异常的处理方式有:捕获处理(try…catch…finally),声明抛出(throw/throws)。如果当前方法适合处理,则捕获处理;如果当前方法不适合处理,则声明抛出。

2.异常规划

1.为了便于统一管理自定义异常,应先创建com.cy.store.service.ex.ServiceException自定义异常的基类异常,继承自RuntimeException类,并从父类生成子类的五个构造方法。

package com.cy.store.service.ex;

/** 业务异常的基类 */
public class ServiceException extends RuntimeException {
	// Override Methods...  
}

2.当用户进行注册时,可能会因为用户名被占用而导致无法正常注册,此时需要抛出用户名被占用的异常,因此可以设计一个用户名重复的com.cy.store.service.ex.UsernameDuplicateException异常类,继承自ServiceException类,并从父类生成子类的五个构造方法。

package com.cy.store.service.ex;

/** 用户名重复的异常 */
public class UsernameDuplicateException extends ServiceException {
    // Override Methods...
}

3.在用户进行注册时,会执行数据库的INSERT操作,该操作也是有可能失败的。则创建cn.tedu.store.service.ex.InsertException`异常类,继承自ServiceException类,并从父类生成子类的5个构造方法。

package com.cy.store.service.ex;

/** 插入数据的异常 */
public class InsertException extends ServiceException {
    // Override Methods...
}

4.所有的自定义异常,都应是RuntimeException的子孙类异常。项目中目前异常的继承结构是见下。

RuntimeException
	-- ServiceException
		-- UsernameDuplicateException
		-- InsertException
4.3 接口与抽象方法

1.先创建com.cy.store.service.IUserService业务层接口,并在接口中添加抽象方法。

package com.cy.store.service;
import com.cy.store.entity.User;

/** 处理用户数据的业务层接口 */
public interface IUserService {
    /**
     * 用户注册
     * @param user 用户数据
     */
    void reg(User user);
}

2.创建业务层接口目的是为了解耦。关于业务层的抽象方法的设计原则。

1.仅以操作成功为前提来设计返回值类型,不考虑操作失败的情况;
2.方法名称可以自定义,通常与用户操作的功能相关;
3.方法的参数列表根据执行的具体业务功能来确定,需要哪些数据就设计哪些数据。通常情况下,参数需要足以调用持久层对应的相关功能;同时还要满足参数是客户端可以传递给控制器的;
4.方法中使用抛出异常的方式来表示操作失败。
4.4 实现抽象方法

1.创建com.cy.store.service.impl.UserServiceImpl业务层实现类,并实现IUserService接口。在类之前添加@Service注解,并在类中添加持久层UserMapper对象。

package com.cy.store.service.impl;
import com.cy.store.entity.User;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/** 处理用户数据的业务层实现类 */
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public void reg(User user) {
        // TODO
    }
}

2.UserServiceImpl类需要重写IUserService接口中的抽象方法,实现步骤大致是:

@Override
public void reg(User user) {
	// 根据参数user对象获取注册的用户名
	// 调用持久层的User findByUsername(String username)方法,根据用户名查询用户数据
	// 判断查询结果是否不为null
	// 是:表示用户名已被占用,则抛出UsernameDuplicateException异常
	
	// 创建当前时间对象
	// 补全数据:加密后的密码
	// 补全数据:盐值
	// 补全数据:isDelete(0)
	// 补全数据:4项日志属性

	// 表示用户名没有被占用,则允许注册
	// 调用持久层Integer insert(User user)方法,执行注册并获取返回值(受影响的行数)
	// 判断受影响的行数是否不为1
	// 是:插入数据时出现某种错误,则抛出InsertException异常
	
}

3.reg()方法的具体实现过程。

@Service
public class UserServiceImpl implements IUserService {
	@Autowired
	private UserMapper userMapper;
	
	@Override
	public void reg(User user) {
		// 根据参数user对象获取注册的用户名
		String username = user.getUsername();
		// 调用持久层的User findByUsername(String username)方法,根据用户名查询用户数据
		User result = userMapper.findByUsername(username);
		// 判断查询结果是否不为null
		if (result != null) {
			// 是:表示用户名已被占用,则抛出UsernameDuplicateException异常
			throw new UsernameDuplicateException("尝试注册的用户名[" + username + "]已经被占用");
		}
		
		// 创建当前时间对象
		Date now = new Date();
		// 补全数据:加密后的密码
		String salt = UUID.randomUUID().toString().toUpperCase();
		String md5Password = getMd5Password(user.getPassword(), salt);
		user.setPassword(md5Password);
		// 补全数据:盐值
		user.setSalt(salt);
		// 补全数据:isDelete(0)
		user.setIsDelete(0);
		// 补全数据:4项日志属性
		user.setCreatedUser(username);
		user.setCreatedTime(now);
		user.setModifiedUser(username);
		user.setModifiedTime(now);
		
		// 表示用户名没有被占用,则允许注册
		// 调用持久层Integer insert(User user)方法,执行注册并获取返回值(受影响的行数)
		Integer rows = userMapper.insert(user);
		// 判断受影响的行数是否不为1
		if (rows != 1) {
			// 是:插入数据时出现某种错误,则抛出InsertException异常
			throw new InsertException("添加用户数据出现未知错误,请联系系统管理员");
		}
	}

	/**
	 * 执行密码加密
	 * @param password 原始密码
	 * @param salt 盐值
	 * @return 加密后的密文
	 */
	private String getMd5Password(String password, String salt) {
		/*
		 * 加密规则:
		 * 1、无视原始密码的强度
		 * 2、使用UUID作为盐值,在原始密码的左右两侧拼接
		 * 3、循环加密3次
		 */
		for (int i = 0; i < 3; i++) {
			password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
		}
		return password;
	}
}

4.完成后在src/test/java下创建com.cy.store.service.UserServiceTests测试类,编写并执行用户注册业务层的单元测试。

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTests {
	@Autowired
	private IUserService iUserService;
	
	@Test
	public void reg() {
		try {
			User user = new User();
			user.setUsername("lower");
			user.setPassword("123456");
			user.setGender(1);
			user.setPhone("17858802974");
			user.setEmail("lower@tedu.cn");
			user.setAvatar("xxxx");
			iUserService.reg(user);
			System.out.println("注册成功!");
		} catch (ServiceException e) {
			System.out.println("注册失败!" + e.getClass().getSimpleName());
            System.out.println(e.getMessage());
		}
	}
}
4.5 密码加密介绍

密码加密可以有效的防止数据泄密后带来的账号安全问题。通常,程序员不需要考虑加密过程中使用的算法,因为已经存在非常多成熟的加密算法可以直接使用。但是所有的加密算法都不适用于对密码进行加密,因为加密算法都是可以进行逆向运算的。即:如果能够获取加密过程中所有的参数,就可以根据密文得到原文。

对密码进行加密时,需使用消息摘要算法。消息摘要算法的特点是:

1.原文相同时,使用相同的摘要算法得到的摘要数据一定相同;
2.使用相同的摘要算法进行运算,无论原文的长度是多少,得到的摘要数据长度是固定的;
3.如果摘要数据相同,则原文几乎相同,但也可能不同,可能性极低。

不同的原文,在一定的概率上能够得到相同的摘要数据,发生这种现象时称为碰撞。

以MD5算法为例,运算得到的结果是128位的二进制数。在密码的应用领域中,通常会限制密码长度的最小值和最大值,可是密码的种类是有限的,发生碰撞在概率上可以认为是不存在的。

常见的摘要算法有SHA(Secure Hash Argorithm)家族和MD(Message Digest)系列的算法。

关于MD5算法的破解主要来自两方面。一个是王小云教授的破解,学术上的破解其实是研究消息摘要算法的碰撞,也就是更快的找到两个不同的原文却对应相同的摘要,并不是假想中的“根据密文逆向运算得到原文”。另一个是所谓的“在线破解”,是使用数据库记录大量的原文与摘要的对应关系,当尝试“破解”时本质上是查询这个数据库,根据摘要查询原文。

为进一步保障密码安全,需满足以下加密规则:

1.要求用户使用安全强度更高的原始密码;
2.加盐;
3.多重加密;
4.综合以上所有应用方式。

5 用户-注册-控制器

5.1 创建响应结果类

创建com.cy.store.util.JsonResult响应结果类型。

package com.cy.store.util;
import java.io.Serializable;

/**
 * 响应结果类
 * @param <E> 响应数据的类型
 */
public class JsonResult<E> implements Serializable {
    /** 状态码 */
    private Integer state;
    /** 状态描述信息 */
    private String message;
    /** 数据 */
    private E data;

    public JsonResult() {
        super();
    }

    public JsonResult(Integer state) {
        super();
        this.state = state;
    }

    /** 出现异常时调用 */
    public JsonResult(Throwable e) {
        super();
        // 获取异常对象中的异常信息
        this.message = e.getMessage();
    }

    public JsonResult(Integer state, E data) {
        super();
        this.state = state;
        this.data = data;
    }

    // Generate: Getter and Setter
}
5.2 设计请求

设计用户提交的请求,并设计响应的方式:

请求路径:/users/reg
请求参数:User user
请求类型:POST
响应结果:JsonResult<Void>
5.3 处理请求

1.创建com.cy.store.controller.UserController控制器类,在类的声明之前添加@RestController和@RequestMapping(“users”)注解,在类中添加IUserService业务对象并使用@Autowired注解修饰。

package com.cy.store.controller;
import com.cy.store.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/** 处理用户相关请求的控制器类 */
@RestController
@RequestMapping("users")
public class UserController {
    @Autowired
    private IUserService userService;
}

2.然后在类中添加处理请求的用户注册方法。

@RequestMapping("reg")
public JsonResult<Void> reg(User user) {
    // 创建返回值
    JsonResult<Void> result = new JsonResult<Void>();
    try {
        // 调用业务对象执行注册
        userService.reg(user);
        // 响应成功
        result.setState(200);
    } catch (UsernameDuplicateException e) {
        // 用户名被占用
        result.setState(4000);
        result.setMessage("用户名已经被占用");
    } catch (InsertException e) {
        // 插入数据异常
        result.setState(5000);
        result.setMessage("注册失败,请联系系统管理员");
    }
    return result;
}

3.完成后启动项目,打开浏览器访问http://localhost:8080/users/reg?username=controller&password=123456请求进行测试。

{
    state: 200,
    message: null,
    data: null
}
5.4 控制器层的调整

1.然后创建提供控制器类的基类com.cy.store.controller.BaseController,在其中定义表示响应成功的状态码及统一处理异常的方法。

@ExceptionHandler注解用于统一处理方法抛出的异常。当我们使用这个注解时,需要定义一个异常的处理方法,再给这个方法加上@ExceptionHandler注解,这个方法就会处理类中其他方法(被@RequestMapping注解)抛出的异常。@ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常。

package com.cy.store.controller;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.ServiceException;
import com.cy.store.service.ex.UsernameDuplicateException;
import com.cy.store.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;

/** 控制器类的基类 */
public class BaseController {
    /** 操作成功的状态码 */
    public static final int OK = 200;

    /** @ExceptionHandler用于统一处理方法抛出的异常 */
    @ExceptionHandler(ServiceException.class)
    public JsonResult<Void> handleException(Throwable e) {
        JsonResult<Void> result = new JsonResult<Void>(e);
        if (e instanceof UsernameDuplicateException) {
            result.setState(4000);
        } else if (e instanceof InsertException) {
            result.setState(5000);
        }
        return result;
    }
}

2.最后简化UserController控制器类中的用户注册reg()方法的代码。

/** 处理用户相关请求的控制器类 */
@RestController
@RequestMapping("users")
public class UserController extends BaseController {
    @Autowired
    private IUserService userService;

    @RequestMapping("reg")
    public JsonResult<Void> reg(User user) {
        // 调用业务对象执行注册
        userService.reg(user);
        // 返回
        return new JsonResult<Void>(OK);
    }
}

3.完成后启动项目,打开浏览器访问http://localhost:8080/users/reg?username=controller&password=123456请求进行测试。

6 用户-注册-前端页面

1.将电脑商城前端资源页面pages文件夹下的静态资源:bootstrap3、css、images、js、web、index.html相关的资源复制到项目src/main/resources/static文件夹下。如图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9LeHA40-1646190006947)(img/2.png)]

2.在register.html页面中body标签内部的最后,添加script标签用于编写JavaScript程序。请求的url中需要添加项目的访问名称。

serialize()方法通过序列化表单值,创建URL编码文本字符串。

<script type="text/javascript">
    $("#btn-reg").click(function() {
        console.log($("#form-reg").serialize());
        $.ajax({
            url: "/users/reg",
            type: "POST",
            data: $("#form-reg").serialize(),
            dataType: "json",
            success: function(json) {
                if (json.state == 200) {
                    alert("注册成功!");
                    // location.href = "login.html";
                } else {
                    alert("注册失败!" + json.message);
                }
            }
        });
	});
</script>

3.完成后启动项目,打开浏览器访问http://localhost:8080/web/register.html页面并进行注册。

注意:由于没有验证数据,即使没有填写用户名或密码,也可以注册成功。

用户登录

1 用户-登录-持久层

1.1 规划需要执行的SQL语句

用户登录功能需要执行的SQL语句是根据用户名查询用户数据,再判断密码是否正确。SQL语句大致是:

SELECT * FROM t_user WHERE username=?

说明:以上SQL语句对应的后台开发已经完成,无需再次开发。

1.2 接口与抽象方法

说明:无需再次开发。

1.3 配置SQL映射

说明:无需再次开发。

2 用户-登录-业务层

2.1 规划异常

1.如果用户名不存在则登录失败,抛出com.cy.store.service.ex.UserNotFoundException异常,并从父类生成子类的五个构造方法。

package com.cy.store.service.ex;

/** 用户数据不存在的异常 */
public class UserNotFoundException extends ServiceException {
    // Override Methods...
}

2.如果用户的isDelete字段的值为1,则表示当前用户数据被标记为“已删除”,需进行登录失败操作同时抛出UserNotFoundException。

3.如果密码错误则进行登录失败操作,同时抛出com.cy.store.service.ex.PasswordNotMatchException异常。

package com.cy.store.service.ex;

/** 密码验证失败的异常 */
public class PasswordNotMatchException extends ServiceException {
    // Override Methods...
}

4.创建以上UserNotFoundException和PasswordNotMatchException异常类,以上异常类应继承自ServiceException类。

2.2 接口与抽象方法

在IUserService接口中添加登录功能的抽象方法。

/**
 * 用户登录
 * @param username 用户名
 * @param password 密码
 * @return 登录成功的用户数据
 */
User login(String username, String password);

当登录成功后需要获取该用户的id,以便于后续识别该用户的身份,并且还需要获取该用户的用户名、头像等数据,用于显示在软件的界面中,需使用可以封装用于id、用户名和头像的数据的类型来作为登录方法的返回值类型。

2.3 实现抽象方法

1.在UserServiceImpl类中添加login(String username, String password)方法并分析业务逻辑。

@Override
public User login(String username, String password) {
	// 调用userMapper的findByUsername()方法,根据参数username查询用户数据
	
	// 判断查询结果是否为null
	// 是:抛出UserNotFoundException异常
	
	// 判断查询结果中的isDelete是否为1
	// 是:抛出UserNotFoundException异常
	
	// 从查询结果中获取盐值
	// 调用getMd5Password()方法,将参数password和salt结合起来进行加密
	// 判断查询结果中的密码,与以上加密得到的密码是否不一致
	// 是:抛出PasswordNotMatchException异常
	
	// 创建新的User对象
	// 将查询结果中的uid、username、avatar封装到新的user对象中
	// 返回新的user对象
	
	return null;
}

2.login(String username, String password)方法中代码的具体实现。

@Override
public User login(String username, String password) {
    // 调用userMapper的findByUsername()方法,根据参数username查询用户数据
    User result = userMapper.findByUsername(username);
    // 判断查询结果是否为null
    if (result == null) {
        // 是:抛出UserNotFoundException异常
        throw new UserNotFoundException("用户数据不存在的错误");
    }

    // 判断查询结果中的isDelete是否为1
    if (result.getIsDelete() == 1) {
        // 是:抛出UserNotFoundException异常
        throw new UserNotFoundException("用户数据不存在的错误");
    }

    // 从查询结果中获取盐值
    String salt = result.getSalt();
    // 调用getMd5Password()方法,将参数password和salt结合起来进行加密
    String md5Password = getMd5Password(password, salt);
    // 判断查询结果中的密码,与以上加密得到的密码是否不一致
    if (!result.getPassword().equals(md5Password)) {
        // 是:抛出PasswordNotMatchException异常
        throw new PasswordNotMatchException("密码验证失败的错误");
    }

    // 创建新的User对象
    User user = new User();
    // 将查询结果中的uid、username、avatar封装到新的user对象中
    user.setUid(result.getUid());
    user.setUsername(result.getUsername());
    user.setAvatar(result.getAvatar());
    // 返回新的user对象
    return user;
}

3.完成后在UserServiceTests中编写并完成单元测试。

@Test
public void login() {
    try {
        String username = "lower";
        String password = "123456";
        User user = iUserService.login(username, password);
        System.out.println("登录成功!" + user);
    } catch (ServiceException e) {
        System.out.println("登录失败!" + e.getClass().getSimpleName());
        System.out.println(e.getMessage());
    }

注意:不要使用错误的数据尝试登录,例如早期通过持久层测试新增用户的数据,将这些数据从表中删除。

3 用户-登录-控制器

3.1 处理异常

处理用户登录功能时,在业务层抛出了UserNotFoundException和PasswordNotMatchException异常,而这两个异常均未被处理过。则应在BaseController类的处理异常的方法中,添加这两个分支进行处理。

@ExceptionHandler(ServiceException.class)
public JsonResult<Void> handleException(Throwable e) {
	JsonResult<Void> result = new JsonResult<Void>(e);
	if (e instanceof UsernameDuplicateException) {
		result.setState(4000);
	} else if (e instanceof UserNotFoundException) {
		result.setState(4001);
	} else if (e instanceof PasswordNotMatchException) {
		result.setState(4002);
	} else if (e instanceof InsertException) {
		result.setState(5000);
	}
	return result;
}
3.2 设计请求

设计用户提交的请求,并设计响应的方式:

请求路径:/users/login
请求参数:String username, String password
请求类型:POST
响应结果:JsonResult<User>
3.3 处理请求

1.在UserController类中添加处理登录请求的login(String username, String password)方法。

@RequestMapping("login")
public JsonResult<User> login(String username, String password) {
	// 调用业务对象的方法执行登录,并获取返回值
	
	// 将以上返回值和状态码OK封装到响应结果中并返回
	
	return null;
}

2.处理登录请求的login(String username, String password)方法代码具体实现。

@RequestMapping("login")
public JsonResult<User> login(String username, String password) {
	// 调用业务对象的方法执行登录,并获取返回值
	User data = userService.login(username, password);
	// 将以上返回值和状态码OK封装到响应结果中并返回
	return new JsonResult<User>(OK, data);
}

4.完成后启动项目,访问http://localhost:8080/users/login?username=Tom&password=1234请求进行登录。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-usEnlbry-1646190006949)(img/4.png)]

4 用户-登录-前端页面

1.在login.html页面中body标签内部的最后,添加script标签用于编写JavaScript程序。

<script type="text/javascript">
    $("#btn-login").click(function() {
    $.ajax({
        url: "/users/login",
        type: "POST",
        data: $("#form-login").serialize(),
        dataType: "json",
        success: function(json) {
            if (json.state == 200) {
                alert("登录成功!");
                location.href = "index.html";
            } else {
                alert("登录失败!" + json.message);
            }
        }
    });
});
</script>

2.完成后启动项目,打开浏览器访问http://localhost:8080/web/login.html页面并进行登录。

拦截器

在Spring MVC中拦截请求是通过处理器拦截器HandlerInterceptor来实现的,它拦截的目标是请求的地址。在Spring MVC中定义一个拦截器,需要实现HandlerInterceptor接口。

1 HandlerInterceptor

1.1 preHandle()方法

该方法将在请求处理之前被调用。SpringMVC中的Interceptor是链式的调用,在一个应用或一个请求中可以同时存在多个Interceptor。每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle()方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值类型,当返回false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值true时,就会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时,就会调用当前请求的Controller方法。

1.2 postHandle()方法

该方法将在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。postHandle方法被调用的方向跟preHandle是相反的,也就是说先声明的Interceptor的postHandle方法反而会后执行。如果当前Interceptor的preHandle()方法返回值为false,则此方法不会被调用。

1.3 afterCompletion()方法

该方法将在整个当前请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作。如果当前Interceptor的preHandle()方法返回值为false,则此方法不会被调用。

2 WebMvcConfigurer

在SpringBoot项目中,如果想要自定义一些Interceptor、ViewResolver、MessageConverter,该如何实现呢?在SpringBoot 1.5版本都是靠重写WebMvcConfigurerAdapter类中的方法来添加自定义拦截器、视图解析器、消息转换器等。而在SpringBoot 2.0版本之后,该类被标记为@Deprecated。因此我们只能靠实现WebMvcConfigurer接口来实现。

WebMvcConfigurer接口中的核心方法之一addInterceptors(InterceptorRegistry registry)方法表示添加拦截器。主要用于进行用户登录状态的拦截,日志的拦截等。

  • addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例

  • addPathPatterns:用于设置拦截器的过滤路径规则;addPathPatterns(“/**”)对所有请求都拦截

  • excludePathPatterns:用于设置不需要拦截的过滤规则

public interface WebMvcConfigurer {
    // ...
    default void addInterceptors(InterceptorRegistry registry) {
   	}
}

3 项目添加拦截器功能

1.分析:项目中很多操作都是需要先登录才可以执行的,如果在每个请求处理之前都编写代码检查Session中有没有登录信息,是不现实的。所以应使用拦截器解决该问题。

2.创建拦截器类com.cy.store.interceptor.LoginInterceptor,并实现org.springframework.web.servlet.HandlerInterceptor接口。

package com.cy.store.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** 定义处理器拦截器 */
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getSession().getAttribute("uid") == null) {
            response.sendRedirect("/web/login.html");
            return false;
        }
        return true;
    }
}

3.创建LoginInterceptorConfigurer拦截器的配置类并实现org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口,配置类需要添加@Configruation注解修饰。

package com.cy.store.config;
import com.cy.store.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;

/** 注册处理器拦截器 */
@Configuration
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
    /** 拦截器配置 */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 创建拦截器对象
        HandlerInterceptor interceptor = new LoginInterceptor();

        // 白名单
        List<String> patterns = new ArrayList<String>();
        patterns.add("/bootstrap3/**");
        patterns.add("/css/**");
        patterns.add("/images/**");
        patterns.add("/js/**");
        patterns.add("/web/register.html");
        patterns.add("/web/login.html");
        patterns.add("/web/index.html");
        patterns.add("/web/product.html");
        patterns.add("/users/reg");
        patterns.add("/users/login");
        patterns.add("/districts/**");
        patterns.add("/products/**");

        // 通过注册工具添加拦截器
        registry.addInterceptor(interceptor).addPathPatterns("/**").excludePathPatterns(patterns);
    }
}

会话

1.重新构建login()方法,登录成功后将uid和username存入到HttpSession对象中。

@RequestMapping("login")
public JsonResult<User> login(String username, String password, HttpSession session) {
    // 调用业务对象的方法执行登录,并获取返回值
    User data = userService.login(username, password);

    //登录成功后,将uid和username存入到HttpSession中
    session.setAttribute("uid", data.getUid());
    session.setAttribute("username", data.getUsername());
    // System.out.println("Session中的uid=" + getUidFromSession(session));
    // System.out.println("Session中的username=" + getUsernameFromSession(session));

    // 将以上返回值和状态码OK封装到响应结果中并返回
    return new JsonResult<User>(OK, data);
}

2.在父类BaseController中添加从HttpSession对象中获取uid和username的方法,以便于后续快捷的获取这两个属性的值。

/**
 * 从HttpSession对象中获取uid
 * @param session HttpSession对象
 * @return 当前登录的用户的id
 */
protected final Integer getUidFromSession(HttpSession session) {
	return Integer.valueOf(session.getAttribute("uid").toString());
}

/**
 * 从HttpSession对象中获取用户名
 * @param session HttpSession对象
 * @return 当前登录的用户名
 */
protected final String getUsernameFromSession(HttpSession session) {
	return session.getAttribute("username").toString();
}
基础学习1:Springboot+MyBatis实现简单的登录功能 学习springboot和mybatis的第一天 阅读详情

相关推荐

SpringBoot 用户登录系统

文章目录SpringBoot 用户登录一、MySQL数据库二、依赖二、前端模板三、后端代码运行结果 SpringBoot 用户登录 一、MySQL数据库 字段 id 字段 username 字段 password create database springboot charset=utf8; use springboot; create table user(id int primary key auto_increment,username varchar(12),password varchar(1

qq_53452284的博客 1386

SpringBoot实现简单的登录注册(超级详细)

第一步:建立简单的项目 第二步:建一个简单的数据表 第三步:配置文件如下: 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://mav

我的青春一去不复返 5万+

SpringBoot与MyBatis整合实现用户注册登录系统实战项目

简介:本项目“SpringBoot与MyBatis整合实现用户注册登录”是一个典型的Java Web全栈开发实践,聚焦于Spring Boot和MyBatis框架的深度融合。Spring Boot通过“约定优于配置”理念简化项目搭建与组件管理,自动集成Web服务器、数据源和事务控制;MyBatis作为持久层框架,支持自定义SQL操作,高效完成数据库CRUD交互。

weixin_42611310的博客 933

springboot 实现登录注册

本文将整合简单的登录注册功能。

qq_45726327的博客 3665

SpringBoot+MyBatis用户注册登录实战工程:IDEA一键导入,含MySQL建表脚本与完整目录结构

Java新手可直接上手的SpringBoot整合MyBatis项目,专注实现用户注册、登录两大核心功能。项目基于SpringBoot 2.x和MyBatis构建,使用标准Maven结构,已配置好spring-boot-starter-web、spring-boot-starter-jdbc、mybatis-spring-boot-starter及mysql-connector-java等必要依赖。附带hyon.sql建表脚本,支持快速初始化MySQL数据库,包含用户表设计、密码插入与查询校验逻辑。工程目录规

grafana6viz的博客 167

全栈音乐网站开发项目:Spring Boot+Mybatis+Vue.js实战

后端服务是现代Web应用的核心,它处理前端发来的请求,并返回相应的数据或执行特定的操作。Spring Boot以其“约定优于配置”的理念,大大简化了基于Spring框架的项目搭建和开发过程。通过Spring Initializr可以快速生成项目结构,而Maven或Gradle则作为依赖管理和构建工具,是现代Java开发的标配。MyBatis是一个优秀的持久层框架,支持定制化SQL、存储过程以及高级映射。它避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。

weixin_42514750的博客 1144

Spring Boot 实战(四):MySQL + MyBatis 接入,打通用户注册最小闭环

Spring Boot 项目创建Controller 接口开发DTO 参数接收与校验Service 分层Result 统一返回全局异常处理日志但目前还有一个核心问题:数据没有真正落库也就是说,现在的接口只是“能跑”,但还不是真正的后端系统。本篇开始,我们接入:把项目真正打通成:新建:@Data新建:@MapperMySQL 没启动数据库不存在用户名密码错误没加 MySQL 驱动。

Mark Wu 的博客 490

VASP+机器学习快速收敛AIMD

本文介绍了如何利用VASP结合机器学习技术快速收敛AIMD(从头算分子动力学)模拟。首先,通过训练机器学习力场,生成关键文件如ML_ABN(包含结构数据集)、ML_FFN(存储回归结果)和ML_LOGFILE(记录训练日志)。接着,配置INCAR文件以设置计算参数,包括并行化、分子动力学和机器学习相关参数。KPOINTS文件用于定义K点网格。计算完成后,备份CONTCAR文件作为后续模拟的初始结构。最后,利用训练好的力场文件进行纯机器学习力场分子动力学模拟,并提交任务执行。

qq_44812865的博客 1405

计算机毕设 SpringBoot+Vue兼职招聘系统 校园兼职招聘系统 招聘网站管理系统 大学生兼职招聘系统Java Vue MySQL数据库 远程调试 代码讲解

计算机毕设 SpringBoot+Vue兼职招聘系统 校园兼职招聘系统 招聘网站管理系统 大学生兼职招聘系统

BYSJLG的博客 741

硬件工程师必备核心知识点

硬件设计是一项系统化工程活动,涉及元器件选型、电源设计、信号完整性、EMC和PCB工艺等多个环节

硬核科技 338

重磅!Repo Wiki!

RepoWiki是Qoder平台的智能文档生成功能,通过静态代码分析自动提取项目架构、模块依赖和核心逻辑,并持续追踪代码变更保持文档同步。其工作流程分为四个阶段:1)代码扫描与AST解析;2)结构分析与依赖映射;3)文档智能生成;4)增量更新机制。支持多种编程语言,能识别设计模式并推断技术决策,适用于新项目搭建和遗留系统维护。优势在于降低理解成本和提高开发效率,但存在文件数量限制和依赖Git的局限性。典型应用场景包括自动生成架构图、API手册和团队知识沉淀。

ayusong870的博客 4021

计算机基于springboot的校园商铺系统

计算机基于springboot的校园商铺系统

m0_73272351的博客 92

MicroLogix1400编程(使用RSLogix 500 + RSLinx Classic Lite)

MicroLogix1400编程

noscallion的博客 1万+

计算机毕业设计之基于SpringBoot+PPT+VUE实现博客系统

文件内容可以为图片笔记,视频内容,可以用来进行文件的及时存储以及更新,删除,长期的存储,对不同分类的文件进行整理筛选。3.文章管理模块:主要功能为文章的新建,删除,更新,搜索,分页文章数据五大模块,进行用户个人的文章内容的撰写,文章的编辑,可以使用富文本,图片视频的上传使用。2.分类管理模块:主要功能为分类的新建,删除,更新,搜索分页分类数据五大模块,进行博客全局的分类整理管理以及分页展示各种分类数据。1.用户模块的管理:包括(用户的注册,用户的登陆,,新用户邀请,个人信息的更新,用户的搜索)五大模块。

2301_79424183的博客 485

告别玄学调参:手把手教你用20元件模型搞定GaN HEMT高频仿真(附S参数提取避坑指南)

本文详细介绍了如何使用20元件模型优化GaN HEMT高频仿真,解决传统16元件模型在10GHz以上频段的误差问题。通过S参数提取避坑指南和实战案例,帮助工程师精准建模分布式电极效应和寄生反馈路径,提升射频电路设计效率。

weixin_42523693的博客 441

告别全局搜索!7种SRP-PHAT快速算法实战对比:哪个更适合你的麦克风阵列?

本文深入探讨了7种SRP-PHAT改进算法在声源定位领域的实战表现,包括SRC、CFRC、SPF等方案。通过对比计算效率、定位精度和环境鲁棒性等核心指标,为不同场景(如会议室、智能家居、工业机器人)提供算法选型建议,帮助工程师优化麦克风阵列性能。

weixin_42546392的博客 397

计算机毕设 Springboot+Vue在线动漫系统 在线会员制动漫系统 在线播放动漫系统 动漫网系统Java Vue MySQL数据库 远程调试 代码讲解

计算机毕设 Springboot+Vue在线动漫系统 在线会员制动漫系统 在线播放动漫系统 动漫网系统

BYSJLG的博客 171

常见消息队列:ActiveMQ、RabbitMQ、RocketMQ、Kafka的区别总结

常见消息队列:ActiveMQ、RabbitMQ、RocketMQ、Kafka的区别。

qq_30713721的博客 3471

计算机毕设 SpringBoot 校园志愿者管理系统 志愿者管理系统 志愿者信息管理系统Java Vue MySQL数据库 远程调试 代码讲解

计算机毕设 SpringBoot 校园志愿者管理系统 志愿者管理系统 志愿者信息管理系统

weixin_72790115的博客 398

交换机初始化配置详解

MGMT或称MGT(Management Port,管理端口)是一个可以连接计算机的三层主机端口,如下图所示。只需使用一条双绞线跳线,就可以将计算机的以太网端口与交换机的MGMT端口连接在一起,实现对交换机的配置与管理。也就是说,可以使用MGMT端口代替Console端口实现对交换机或交换机堆叠的网络管理。MGMT端口既可用于交换机的初始化,也可以在交换机初始化完成后,以Telnet、Web浏览器或Cisco CNA方式进行管理。当将计算机连接到独立的交换机时,连接方式如下图所示。

悦分享 1992

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

前后端分离网上书店系统|SpringBoot+微信小程序+MyBatis+MySQL完整源码+部署教程

直接拿走,意外获得200多套代码,需要的滴我前后端分离网上书店系统|SpringBoot+微信小程序+MyBatis+MySQL完整源码+部署教程(可提供说明文档(通过*AIGC*)

2501_92839909的博客 755

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

前后端分离spring电影订票系统系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程

开源免费分享前后端分离spring电影订票系统系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程可提供说明文档 可以通过*AIGC**技术包括:MySQL、VueJS、ElementUI、(Python或者Java或者.NET)等等*功能如图所示。可以滴我获取详细的视频介绍

yydYYDD的博客 444

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

SpringBoot+SpringMVC+Mybatis+Swagger实现用户注册、登录功能

SpringBoot+SpringMVC+Mybatis+Swagger实现用户注册、登录功能

lidasha_521的博客 1914

SpringBoot+MyBatis+MySql从头到尾完成一个电商项目(五、用户注册、登录)

1、码云新建一个分支user-register 2、用户获取短信验证码otp的接口编写: 首先在UserController中注入HttpServletRequest这样我们可以拿到Session 然后写接口如下: 然后启动程序进行测试结果如下: 再看一下我们的控制台打印出来了电话和验证码,这里只是测试,正规的企业开发不能暴漏用户的验证码!! 3、下面我们通过前后端分离...

MagicMHD的博客 2809

SpringBoot项目实战笔记:电脑商城项目实战(SpringBoot+MyBatis+MySQL)

SpringBoot项目实战笔记:电脑商城项目实战(SpringBoot+MyBatis+MySQL)

weixin_44260350的博客 8281

SpingBoot+Mybatis+IDEA实现用户注册功能

以下是用SpingBoot+Mybatis+IDEA实现用户注册功能的代码,仅供参考。 1、在UserMapping.xml文件中加入以下sql代码 <insert id="Insert" parameterType="wang.doug.frame.model.User"> insert into user (userName,passWord,realName)...

波点兔的博客 1428
上一篇: Linux命令速查:从入门到精通
下一篇: Spring Boot模板引擎配置全攻略
「已注销」
博客等级 码龄7年 158粉丝 76原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值