springboot实现注册加密与登录解密功能(demo)
作者:zuojunZzz 发布时间:2021-12-15 15:10:08
标签:springboot,注册,加密,登录,解密
前情提要:本demo是基于springboot+mybatis-plus实现加密,加密为主,全局异常处理,日志处理为辅,而登录密码加密是每个项目中必须要的,密码不可能明文存入数据,这样没有安全性。
涉及的功能,全局异常处理,日志处理,mybatis-plus实现与数据库的交互,密码加密,restful风格
涉及的工具:IDEA,postman,sqlyog(navicat)
1. 首先我们直接看效果吧,如果你不满意,也就没必要看了
如果这正是你想要的效果呢,那你可以继续看下面的内容了
2. 首先,我们看下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.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jgsu</groupId>
<artifactId>springboot_rsa_encryption</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_encryption</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- WEB依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- mysql的依赖,用于连接数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- lombok依赖,简化set/get方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<!--druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--spring-security实现密码加密-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<!--fastjsoon-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. 创建启动类SpringbootEncryptionApplication
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@SpringBootApplication
@MapperScan("com.jgsu.mapper")
public class SpringbootEncryptionApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootEncryptionApplication.class, args);
}
/**
* 将加密工具类加入IOC容器中,便于加密
* */
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
4.实体类
这里只有用户名和密码(其他数据自己可扩展)
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
@TableId(value = "id",type = IdType.AUTO)
private int id;
private String username;
private String password;
}
5. service层(业务层)
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jgsu.entity.User;
import com.jgsu.exception.DataAddException;
import com.jgsu.exception.DataMatchException;
import com.jgsu.mapper.UserMapper;
import com.jgsu.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
// 数据加密,在启动类中已经注入进IOC容器中
@Autowired
private BCryptPasswordEncoder encoder;
@Override
public User userLogin(String username,String password) {
// mybatis-plus的条件构造器,这里实现根据username查询
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("username", username);
User userLogin = userMapper.selectOne(wrapper);
/**
* encoder.matches(password, userLogin.getPassword(),实现输入的密码与数据库中的密码进
* 行匹配,如果匹配成功则返回匹配的数据给controller层,如果失败则抛异常。
* 为什么没盐,没有解密了?因为这个已经被CryptPasswordEncoder封装好了,
* 在encoder.matches()方进行解密匹配完全帮你封装好了,所以不必考虑,
* 只需要将前端传入的密码与数据库中加密后的密码进行匹配就行。
* **/
if (userLogin != null && encoder.matches(password, userLogin.getPassword())) {
log.info("用户{},登录成功",username);
return userLogin;
} else {
log.error("用户名或密码错误");
throw new DataMatchException("405", "用户名或密码错误");
}
}
@Override
public User userRegister(String username, String password) {
User user = new User();
user.setId(user.getId());
user.setUsername(username);
user.setPassword(encoder.encode(password));
int i = userMapper.insert(user);
if (i == 1){
log.info("用户{}注册成功",username);
return user;
}else {
log.error("服务器发生异常,注册失败");
throw new DataAddException("403","注册失败");
}
}
}
6. mapper层
如果不了解,可以建议去看看mybatis-plus官方文档
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jgsu.entity.User;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
}
7.controller层
import com.jgsu.entity.User;
import com.jgsu.service.UserService;
import com.jgsu.utils.CommonResult;
import com.jgsu.utils.ResultCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
// 注册,基于restful风格
@GetMapping("/register/{username}/{password}")
public CommonResult register(@PathVariable("username") String username,@PathVariable("password") String password){
User user = userService.userRegister(username, password);
if (user != null){
return CommonResult.success(ResultCode.SUCCESS);
}else {
return CommonResult.failed(ResultCode.FAILED);
}
}
// 登录,基于restful风格
@GetMapping("/login/{username}/{password}")
public CommonResult login(@PathVariable("username") String username,@PathVariable("password") String password) {
User userLogin = userService.userLogin(username,password);
if (userLogin != null) {
return CommonResult.success(ResultCode.SUCCESS);
} else {
return CommonResult.failed(ResultCode.USERNAME_OR_PASSWORD_ERROR);
}
}
}
8.配置类(返回json数据的类)
封装错误码的接口
public interface IErrorCode {
long getState();
String getMessage();
}
枚举了一些常用API操作码
public enum ResultCode implements IErrorCode {
/**
* 成功
*/
SUCCESS(200, "ok"),
/**
* 失败
*/
FAILED(500, "server error"),
/**
* 验证过期
*/
VALIDATE_FAILED(404, "undefined"),
/**
* 未登录
*/
UNAUTHORIZED(401, "未登录"),
/**
* 用户名或密码错误
*/
USERNAME_OR_PASSWORD_ERROR(405, "用户名或密码错误"),
/**
* 数据查询错误
*/
DATA_Not_Exist_ERROR(603, "数据不存在"),
/**
* 数据添加出现问题
*/
DATA_ADD_ERROR(604, "数据添加异常"),
/**
* 文件
*/
FILE_ERROR(605, "上传文件出现错误"),
/**
* 数据查询出现问题
*/
IMAGE_ERROR(606, "图片处理出现错误"),
/**
* 权限不够
*/
FORBIDDEN(403, "forbidden");
private long state;
private String stateInfo;
ResultCode(long state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
}
@Override
public long getState() {
return state;
}
@Override
public String getMessage() {
return stateInfo;
}
}
通用返回对象
public class CommonResult<T> {
private long state;
private String stateInfo;
private T data;
public CommonResult() {
}
public CommonResult(long state, String stateInfo, T data) {
this.state = state;
this.stateInfo = stateInfo;
this.data = data;
}
public CommonResult(long state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
}
/**
* 成功返回结果
*
* @param data 获取的数据
*/
public static <T> CommonResult<T> success(T data) {
return new CommonResult<T>(ResultCode.SUCCESS.getState(), ResultCode.SUCCESS.getMessage(), data);
}
/**
* 成功返回结果
* @param data 获取的数据
* @param message 提示信息
* @return
*/
public static <T> CommonResult<T> success(T data, String message) {
return new CommonResult<T>(ResultCode.SUCCESS.getState(), message, data);
}
/**
* 失败返回结果
* @param errorCode 错误码
*/
public static <T> CommonResult<T> failed(IErrorCode errorCode) {
return new CommonResult<T>(errorCode.getState(), errorCode.getMessage(), null);
}
/**
* 失败返回结果
* @param message 提示信息
*/
public static <T> CommonResult<T> failed(String message) {
return new CommonResult<T>(ResultCode.FAILED.getState(), message, null);
}
/**
* 失败返回结果
* @param code
* @param message
*/
public static <T> CommonResult<T> failed(int code, String message) {
return failed(ResultCode.FAILED);
}
/**
* 参数验证失败返回结果
*/
public static <T> CommonResult<T> validateFailed() {
return failed(ResultCode.VALIDATE_FAILED);
}
/**
* 参数验证失败返回结果
* @param message 提示信息
*/
public static <T> CommonResult<T> validateFailed(String message) {
return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getState(), message, null);
}
/**
* 未登录返回结果
*/
public static <T> CommonResult<T> unauthorized(T data) {
return new CommonResult<T>(ResultCode.UNAUTHORIZED.getState(), ResultCode.UNAUTHORIZED.getMessage(), data);
}
/**
* 未授权返回结果
*/
public static <T> CommonResult<T> forbidden(T data) {
return new CommonResult<T>(ResultCode.FORBIDDEN.getState(), ResultCode.FORBIDDEN.getMessage(), data);
}
public long getState() {
return state;
}
public void setState(long state) {
this.state = state;
}
public String getStateInfo() {
return stateInfo;
}
public void setStateInfo(String stateInfo) {
this.stateInfo = stateInfo;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
9. 异常类
全局异常拦截类
@Slf4j
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHander {
@ExceptionHandler(value = Exception.class)
public CommonResult handlerException(Exception e){
if (e instanceof DataAddException){
log.error("【全局异常拦截】DataAddException: 请求方法 {}, 请求路径 {}",((DataAddException)e).getCode(),((DataAddException)e).getMessage());
return CommonResult.failed(ResultCode.DATA_ADD_ERROR);
}else if (e instanceof DataMatchException){
log.error("【全局异常拦截】DataMatchException: 请求方法 {}, 请求路径 {}",((DataMatchException)e).getCode(),((DataMatchException)e).getMessage());
return CommonResult.failed(ResultCode.USERNAME_OR_PASSWORD_ERROR);
} else {
log.error("服务器内部出错{}",e);
return CommonResult.failed(ResultCode.FAILED);
}
}
}
自定义数据添加异常类
public class DataAddException extends RuntimeException {
private String code;
private String message;
public DataAddException(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
自定义数据匹配异常类
public class DataMatchException extends RuntimeException {
private String code;
private String message;
public DataMatchException(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
以上是实现该功能的所有类,当然进行密码加密处理的类只涉及service层以及启动类,其他类为基本类,如果你只想了解怎么进行加密处理存入数据库以及怎么登陆就看看service层就行。
来源:https://juejin.im/post/5e5375a0e51d45270218f51f


猜你喜欢
- 一、简介构造函数,基本用法是在类对象声明的时候完成初始化工作。二、实例构造函数1、构造函数的名字与类名相同。2、使用 new 表达式创建类的
- DrawingContext比较类似WinForm中的Graphics 类,是基础的绘图对象,用于绘制各种图形,它主要API有如下
- 基于 kotlin/coroutine/retrofit/jetpack 打造,100来行代码,用法超级简单舒适设置默认Retrofit工厂
- 前言内存泄漏简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系
- 题目一 解法class Solution { public int findLengthOfLCIS(i
- 简介Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生
- maven周期maven的生命周期不止package,compile,clean。其实这是主要部分。以下截图其实展示的是maven的所有周期
- Java内部类一、 含义在Java编程语言里,程序是由类(class)构建而成的。在一个类的内部也可以声明类,我们把这样的类叫做内部类。二、
- 话不多说,请看代码/// <summary>/// 判断字符串是否是数字/// </summary>public s
- 二叉排序树,又称为二叉查找树。它或者是一颗空树,或者是具有下列性质的二叉树:若它的左子树不为空。则左子树上所有的结点的值均小于跟的结点值若它
- 在Spring4之后,要使用注解开发,必须要保证aop的包导入了使用注解需要导入context约束,增加注解的支持!<?xml ver
- 一、介绍Spring是通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用Spring提供的ThreadPoolTaskE
- 背景在研究规则引擎时,如果规则以文件的形式存储,那么就需要监听指定的目录或文件来感知规则是否变化,进而进行加载。当然,在其他业务场景下,比如
- 问题引出:最近开了新项目,项目中用到了数据字典,列表查询数据返回的时候需要手动将code转换为name,到前台展示。项目经理表示可以封装一个
- 也许很多朋友在学习NIO的时候都会感觉有点吃力,对里面的很多概念都感觉不是那么明朗。在进入Java NIO编程之前,我们今天先来讨论一些比较
- 第一种方法string s=abcdeabcdeabcde;string[] sArray=s.Split('c') ;fo
- import java.io.*;import java.text.SimpleDateFormat;import java.util.*;
- 注册中心呢 就是springcloud的一个核心组件 所有微服务的基石 微服务的核心思想就是分布式 所有的服务分开管理 但这些服务分开后该如
- 前言在之前的文章中介绍过线程池的核心原理,在一次面试中面试官让手写线程池,这块知识忘记的差不多了,因此本篇文章做一个回顾。希望能够加深自己的
- 前言日常开发中,我们可能会碰到需要进行防重放与操作幂等的业务,本文记录SpringBoot实现简单防重与幂等防重放,防止数据重复提交操作幂等