栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Security - 14 用户注册(添加用户到数据库)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Security - 14 用户注册(添加用户到数据库)

文章目录

环境用户注册

思路数据库项目结构配置编码测试 参考

环境

操作系统:

Windows 10 x64

集成开发环境:

Spring Tool Suite 4 
Version: 4.14.0.RELEASE
Build Id: 202203131612

Postman(客户端):

Postman for Windows
Version 9.0.9
win32 10.0.19044 / x64
用户注册 思路

要实现用户注册功能,首先要准备一个数据库,此处我选用 MySQL,然后使用 MyBatis-Plus 框架完成添加用户…

数据库

MySQL 数据库表结构:

mysql> describe user;
+-------------------------+--------------+------+-----+-------------------+-----------------------------+
| Field                   | Type         | Null | Key | Default           | Extra                       |
+-------------------------+--------------+------+-----+-------------------+-----------------------------+
| id                      | bigint(20)   | NO   | PRI | NULL              | auto_increment              |
| username                | varchar(256) | NO   |     | NULL              |                             |
| password                | varchar(256) | NO   |     | NULL              |                             |
| account_non_expired     | tinyint(1)   | YES  |     | 1                 |                             |
| account_non_locked      | tinyint(1)   | YES  |     | 1                 |                             |
| credentials_non_expired | tinyint(1)   | YES  |     | 1                 |                             |
| enabled                 | tinyint(1)   | YES  |     | 1                 |                             |
| deleted                 | tinyint(1)   | YES  |     | 0                 |                             |
| create_time             | timestamp    | YES  |     | CURRENT_TIMESTAMP |                             |
| update_time             | timestamp    | YES  |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------------------+--------------+------+-----+-------------------+-----------------------------+
10 rows in set (0.00 sec)

建表语句:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `password` varchar(256) NOT NULL,
  `account_non_expired` tinyint(1) DEFAULT '1' COMMENT 'true (1, default) if the user''s account is valid (ie non-expired), false (0) if no longer valid (ie expired).',
  `account_non_locked` tinyint(1) DEFAULT '1' COMMENT 'true (1, default) if the user is not locked, false (0) otherwise.',
  `credentials_non_expired` tinyint(1) DEFAULT '1' COMMENT 'true (1, default) if the user''s credentials are valid (ie non-expired), false (0) if no longer valid (ie expired).',
  `enabled` tinyint(1) DEFAULT '1' COMMENT 'true (1, default) if the user is enabled, false (0) otherwise.',
  `deleted` tinyint(1) DEFAULT '0' COMMENT 'true (1) if this record is deleted, false (0, default) otherwise.',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
项目结构

参考:Spring Security - 13 - 禁用 CSRF、获取 CSRF 值

配置

修改 pom.xml 配置文件,引入 MyBatis-Plus、MySQL Connector、Validation 等依赖(第 10 ~ 25 行):



    
    
    
        
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.5.1
        
        
        
            mysql
            mysql-connector-java
            runtime
        
        
        
            org.springframework.boot
            spring-boot-starter-validation
        
    

修改 application.yml 配置文件,添加数据源:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.88.158:3307/hello-account?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=true
    username: root
    password: 123456
  thymeleaf:
    cache: false
    
logging:
  level:
    com.mk: debug
编码

用于返回结果的 HttpResult 类:

package com.mk.web.common;

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonInclude;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class HttpResult implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private String message;

    @JsonInclude(value = JsonInclude.Include.NON_EMPTY)
    private Object data;
}

自定义的服务异常 ServiceException 类:

package com.mk.web.exception;

public class ServiceException extends Exception {

    private static final long serialVersionUID = 1L;

    
    public ServiceException() {
        super();
    }

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

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

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

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

常量 Constants 类:

package com.mk.web.constant;

public class Constants {

    private static final String SUCCESS = "成功";
    private static final String FAILURE = "失败";

    private static final String INSERT = "新增";
    private static final String DELETE = "删除";
    private static final String UPDATE = "更新";
    private static final String SELECT = "查询";

    public static final String JDBC_INSERT_SUCCESS = INSERT + SUCCESS;
    public static final String JDBC_INSERT_FAILURE = INSERT + FAILURE;

    public static final String JDBC_DELETE_SUCCESS = DELETE + SUCCESS;
    public static final String JDBC_DELETE_FAILURE = DELETE + FAILURE;

    public static final String JDBC_UPDATE_SUCCESS = UPDATE + SUCCESS;
    public static final String JDBC_UPDATE_FAILURE = UPDATE + FAILURE;

    public static final String JDBC_SELECT_SUCCESS = SELECT + SUCCESS;
    public static final String JDBC_SELECT_FAILURE = SELECT + FAILURE;
}

全局异常处理类:

package com.mk.exception.handler;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mk.web.common.HttpResult;
import com.mk.web.exception.ServiceException;

import lombok.extern.slf4j.Slf4j;


@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    private static final ObjectMapper objectMapper = new ObjectMapper();
    
    
    @ExceptionHandler(value = { ServiceException.class })
    @ResponseBody
    public void serviceExceptionHandler(Exception e, HttpServletResponse response) throws IOException {
        log.error("Service Exception: ", e);
        
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        
        String message = e.getMessage();
        HttpResult result = new HttpResult(message, null);
        
        PrintWriter writer = response.getWriter();
        writer.write(objectMapper.writevalueAsString(result));
        
        writer.flush();
        writer.close();
    }

    // https://blog.csdn.net/qq_43409401/article/details/116017177
    // https://blog.csdn.net/zmflying8177/article/details/100755223
    @ExceptionHandler(value = { BindException.class })
    @ResponseBody
    public void bindExceptionHandler(BindException e, HttpServletResponse response) throws IOException {
        log.error("Bind Exception: ", e);
        
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        HttpResult result = new HttpResult(message, null);
        
        PrintWriter writer = response.getWriter();
        writer.write(objectMapper.writevalueAsString(result));
        
        writer.flush();
        writer.close();
    }
}

实体类 User:

package com.mk.web.console.account.pojo;

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

import javax.validation.constraints.NotBlank;

import org.hibernate.validator.constraints.Length;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonInclude;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;


@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class User extends Model {

    private static final long serialVersionUID = 1L;
    
    public static final String TABLE_NAME = "user";

    public static final String ID = "id";

    public static final String USERNAME = "username";

    public static final String PASSWORD = "password";
    
    public static final String ACCOUNT_NON_EXPIRED = "account_non_expired";

    public static final String ACCOUNT_NON_LOCKED = "account_non_locked";
    
    public static final String CREDENTIALS_NON_EXPIRED = "credentials_non_expired";

    public static final String ENABLED = "enabled";

    public static final String DELETED = "deleted";
    

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @NotBlank(message = "【用户名】不能为空")
    @Length(min = 2, max = 20, message = "【用户名】长度不满足要求")
    private String username;
    
    @NotBlank(message = "【密码】不能为空")
    @Length(min = 8, max = 20, message = "【密码】长度不满足要求")
    private String password;

    
    private Boolean accountNonExpired;

    
    private Boolean accountNonLocked;

    
    private Boolean credentialsNonExpired;

    
    private Boolean enabled;

    
    private Boolean deleted;

    private LocalDateTime createTime;

    private LocalDateTime updateTime;

    @Override
    public Serializable pkVal() {
        return this.id;
    }
}

UserMapper 接口:

package com.mk.web.console.account.mapper;

import com.mk.web.console.account.pojo.User;
import com.baomidou.mybatisplus.core.mapper.baseMapper;


public interface UserMapper extends baseMapper {

}

UserService 服务接口:

package com.mk.web.console.account.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.mk.web.console.account.pojo.User;
import com.mk.web.exception.ServiceException;


public interface UserService extends IService {

    void register(User entity) throws ServiceException;
}

UserService 服务接口的实现类 UserServiceImpl:

package com.mk.web.console.account.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mk.web.console.account.mapper.UserMapper;
import com.mk.web.console.account.pojo.User;
import com.mk.web.console.account.service.UserService;
import com.mk.web.constant.Constants;
import com.mk.web.exception.ServiceException;


@Service("CONSOLE_ACCOUNT_USER_SERVICE")
public class UserServiceImpl extends ServiceImpl implements UserService {
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(User entity) throws ServiceException {
        entity.setId(null);
        entity.setPassword(encodedPassword(entity.getPassword()));
        entity.setAccountNonExpired(null);
        entity.setAccountNonLocked(null);
        entity.setCredentialsNonExpired(null);
        entity.setEnabled(null);
        entity.setDeleted(null);
        entity.setCreateTime(null);
        entity.setUpdateTime(null);
        
        if (!super.save(entity)) {
            throw new ServiceException(Constants.JDBC_INSERT_FAILURE);
        }
    }
    
    private String encodedPassword(String rawPassword) {
        return passwordEncoder.encode(rawPassword);
    }
}

控制器类 UserController:

package com.mk.web.console.account.controller;


import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.mk.web.common.HttpResult;
import com.mk.web.console.account.pojo.User;
import com.mk.web.console.account.service.UserService;
import com.mk.web.exception.ServiceException;

import lombok.extern.slf4j.Slf4j;


@RestController("ACCOUNT_USER_CONTROLLER")
@RequestMapping("/console/account/user")
@Slf4j
public class UserController {
    
    private static final boolean debug = log.isDebugEnabled();
    
    @Autowired
    @Qualifier("CONSOLE_ACCOUNT_USER_SERVICE")
    private UserService userService;
    
    
    @PostMapping(path = "register")
    public HttpResult register(@Valid User entity) throws ServiceException {
        if (debug)
            log.debug("Register: {}", entity);
        
        userService.register(entity);
        
        return new HttpResult("ok", null);
    }
}

修改 WebSecurityConfigurer 配置类,添加允许匿名访问的路径(第 28 ~ 31 行):

package com.mk.security.config.annotation.web.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

//@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin(); // Specifies to support form based authentication.
        
        http.authorizeRequests(customizer -> {
            String anonymous[] = { "/csrf", "/console/account/user/register" };
            
            // Specify that URLs are allowed by anyone.
            customizer.antMatchers(anonymous).permitAll();
            
            // Any request are allowed by any authenticated user.
            customizer.anyRequest().authenticated();
        });
        
//        http.csrf(customizer -> {
//            customizer.disable();
//        });
    }
}

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 mapper 包(第 8 行):

package com.mk;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.mk.web.console.account.mapper")
public class SpringSecurityHelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityHelloApplication.class, args);
    }
}
测试

启动应用,使用 Postman 访问 http://127.0.0.1:8080/console/account/user/register,提交必要的注册信息。成功之后,可以看到返回的信息:

查询数据库,得到:

mysql> select * from user;
+----+----------+--------------------------------------------------------------+---------------------+--------------------+-------------------------+---------+---------+---------------------+---------------------+
| id | username | password                                                     | account_non_expired | account_non_locked | credentials_non_expired | enabled | deleted | create_time         | update_time         |
+----+----------+--------------------------------------------------------------+---------------------+--------------------+-------------------------+---------+---------+---------------------+---------------------+
|  1 | zs       | $2a$10$WDVUU5l3TyjvEiuzeWAIAeizQm4bp3T3J05tIplcB8Rndrj0h5r2i |                   1 |                  1 |                       1 |       1 |       0 | 2022-03-27 07:31:20 | 2022-03-27 07:31:20 |
|  2 | ls       | $2a$10$b1BL/sjoFNxbzs2/Tgcliur5biTd51qB229YT8Uk1m2gPhAePu/wy |                   1 |                  1 |                       1 |       1 |       0 | 2022-03-27 08:37:52 | 2022-03-27 08:37:52 |
+----+----------+--------------------------------------------------------------+---------------------+--------------------+-------------------------+---------+---------+---------------------+---------------------+
2 rows in set (0.00 sec)

如果提交的信息不满足 User 类中 username 和 password 字段的验证要求:

package com.mk.web.console.account.pojo;

// 其他...


@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class User extends Model {
    
    // 其他...

    @NotBlank(message = "【用户名】不能为空")
    @Length(min = 2, max = 20, message = "【用户名】长度不满足要求")
    private String username;
    
    @NotBlank(message = "【密码】不能为空")
    @Length(min = 8, max = 20, message = "【密码】长度不满足要求")
    private String password;

    // 其他...
}

那么,你将得到如下的提示:

参考

Spring Boot - 简单地使用 MyBatis-Plus

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/781198.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号