- Резюме: эта демонстрация основана на springboot + mybatis-plus для достижения шифрования, шифрование является основным, глобальная обработка исключений, обработка журнала дополнена, и шифрование логина-пароля необходимо в каждом проекте, пароль не может храниться в открытых текстовых данных, там нет безопасности.
Задействованные функции, глобальная обработка исключений, обработка журналов, взаимодействие mybatis-plus с базой данных, шифрование паролей, спокойный стиль
Задействованные инструменты: 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. сервисный уровень (бизнес-уровень)
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. картографический слой
Если не разбираетесь, можете предложить посмотреть официальную документацию 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. Уровень контроллера
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; } }
Выше перечислены все классы, которые реализуют эту функцию. Конечно, классы, которые выполняют обработку шифрования паролей, включают только уровень службы и класс запуска. Другие классы являются базовыми классами. Если вы просто хотите знать, как шифровать и хранить в базы данных и как войти в систему, посмотрите на сервисный слой.Просто сделайте это.