v0.01 后端登录实现

v0.0
bruce 2025-02-10 16:40:47 +08:00
parent 5b53d9502a
commit fe3da4b1f8
21 changed files with 926 additions and 6 deletions

29
pom.xml
View File

@ -38,12 +38,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.10.1</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
@ -70,6 +70,25 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- token &jwt 依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,94 @@
package com.bruce.sams.Constant;
/**
*
*
* @author ruoyi
*/
public class HttpStatus
{
/**
*
*/
public static final int SUCCESS = 200;
/**
*
*/
public static final int CREATED = 201;
/**
*
*/
public static final int ACCEPTED = 202;
/**
*
*/
public static final int NO_CONTENT = 204;
/**
*
*/
public static final int MOVED_PERM = 301;
/**
*
*/
public static final int SEE_OTHER = 303;
/**
*
*/
public static final int NOT_MODIFIED = 304;
/**
*
*/
public static final int BAD_REQUEST = 400;
/**
*
*/
public static final int UNAUTHORIZED = 401;
/**
* 访
*/
public static final int FORBIDDEN = 403;
/**
*
*/
public static final int NOT_FOUND = 404;
/**
* http
*/
public static final int BAD_METHOD = 405;
/**
*
*/
public static final int CONFLICT = 409;
/**
*
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
*
*/
public static final int ERROR = 500;
/**
*
*/
public static final int NOT_IMPLEMENTED = 501;
/**
*
*/
public static final int WARN = 601;
}

View File

@ -0,0 +1,35 @@
package com.bruce.sams.Controller;
import com.bruce.sams.service.SysLoginService;
import com.bruce.sams.Utils.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/login")
public class LoginController {
@Autowired
private SysLoginService sysLoginService;
/**
*
*
* @param username
* @param password
* @return AjaxResult token
*/
@PostMapping
public AjaxResult login(@RequestParam String username, @RequestParam String password) {
try {
// 调用登录服务生成Token
String token = sysLoginService.login(username, password);
// 返回成功的响应并包含生成的Token
return AjaxResult.success("登录成功", token);
} catch (Exception e) {
// 如果捕获到异常,返回错误信息
return AjaxResult.error("登录失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,18 @@
package com.bruce.sams.Controller;
import com.bruce.sams.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private SysUserService userService;
public Object addUesr(){
return null;
}
}

View File

@ -0,0 +1,151 @@
package com.bruce.sams.Entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
*
* @TableName sys_user
*/
@TableName(value ="sys_user")
@Data
public class SysUser {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Long userId;
/**
*
*/
private Long roleId;
/**
*
*/
private String nickName;
/**
*
*/
private String userName;
/**
*
*/
private String passwd;
/**
* /
*/
private String schoolId;
/**
*
*/
private Long collegeId;
/**
*
*/
private String email;
/**
*
*/
private String phonenumber;
/**
* 0 1 2
*/
private String sex;
/**
*
*/
private String avatar;
/**
* 0 1
*/
private String status;
/**
*
*/
private String remark;
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
SysUser other = (SysUser) that;
return (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
&& (this.getRoleId() == null ? other.getRoleId() == null : this.getRoleId().equals(other.getRoleId()))
&& (this.getNickName() == null ? other.getNickName() == null : this.getNickName().equals(other.getNickName()))
&& (this.getUserName() == null ? other.getUserName() == null : this.getUserName().equals(other.getUserName()))
&& (this.getPasswd() == null ? other.getPasswd() == null : this.getPasswd().equals(other.getPasswd()))
&& (this.getSchoolId() == null ? other.getSchoolId() == null : this.getSchoolId().equals(other.getSchoolId()))
&& (this.getCollegeId() == null ? other.getCollegeId() == null : this.getCollegeId().equals(other.getCollegeId()))
&& (this.getEmail() == null ? other.getEmail() == null : this.getEmail().equals(other.getEmail()))
&& (this.getPhonenumber() == null ? other.getPhonenumber() == null : this.getPhonenumber().equals(other.getPhonenumber()))
&& (this.getSex() == null ? other.getSex() == null : this.getSex().equals(other.getSex()))
&& (this.getAvatar() == null ? other.getAvatar() == null : this.getAvatar().equals(other.getAvatar()))
&& (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()))
&& (this.getRemark() == null ? other.getRemark() == null : this.getRemark().equals(other.getRemark()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
result = prime * result + ((getRoleId() == null) ? 0 : getRoleId().hashCode());
result = prime * result + ((getNickName() == null) ? 0 : getNickName().hashCode());
result = prime * result + ((getUserName() == null) ? 0 : getUserName().hashCode());
result = prime * result + ((getPasswd() == null) ? 0 : getPasswd().hashCode());
result = prime * result + ((getSchoolId() == null) ? 0 : getSchoolId().hashCode());
result = prime * result + ((getCollegeId() == null) ? 0 : getCollegeId().hashCode());
result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
result = prime * result + ((getPhonenumber() == null) ? 0 : getPhonenumber().hashCode());
result = prime * result + ((getSex() == null) ? 0 : getSex().hashCode());
result = prime * result + ((getAvatar() == null) ? 0 : getAvatar().hashCode());
result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
result = prime * result + ((getRemark() == null) ? 0 : getRemark().hashCode());
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", userId=").append(userId);
sb.append(", roleId=").append(roleId);
sb.append(", nickName=").append(nickName);
sb.append(", userName=").append(userName);
sb.append(", passwd=").append(passwd);
sb.append(", schoolId=").append(schoolId);
sb.append(", collegeId=").append(collegeId);
sb.append(", email=").append(email);
sb.append(", phonenumber=").append(phonenumber);
sb.append(", sex=").append(sex);
sb.append(", avatar=").append(avatar);
sb.append(", status=").append(status);
sb.append(", remark=").append(remark);
sb.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,11 @@
package com.bruce.sams.Exception;
public class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
public CustomException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,12 @@
package com.bruce.sams.Exception.User;
import com.bruce.sams.Exception.CustomException;
/**
*
*/
public class IncorrectPasswordException extends CustomException {
public IncorrectPasswordException(String message) {
super(message);
}
}

View File

@ -0,0 +1,12 @@
package com.bruce.sams.Exception.User;
import com.bruce.sams.Exception.CustomException;
/**
*
*/
public class InsufficientPermissionsException extends CustomException {
public InsufficientPermissionsException(String message) {
super(message);
}
}

View File

@ -0,0 +1,12 @@
package com.bruce.sams.Exception.User;
import com.bruce.sams.Exception.CustomException;
/**
*
*/
public class InvalidInputException extends CustomException {
public InvalidInputException(String message) {
super(message);
}
}

View File

@ -0,0 +1,12 @@
package com.bruce.sams.Exception.User;
import com.bruce.sams.Exception.CustomException;
/**
*
*/
public class UserNotFoundException extends CustomException {
public UserNotFoundException(String message) {
super(message);
}
}

View File

@ -0,0 +1,215 @@
package com.bruce.sams.Utils;
import com.bruce.sams.Constant.HttpStatus;
import java.util.HashMap;
import java.util.Objects;
/**
*
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/** 状态码 */
public static final String CODE_TAG = "code";
/** 返回内容 */
public static final String MSG_TAG = "msg";
/** 数据对象 */
public static final String DATA_TAG = "data";
/**
* AjaxResult 使
*/
public AjaxResult()
{
}
/**
* AjaxResult
*
* @param code
* @param msg
*/
public AjaxResult(int code, String msg)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* AjaxResult
*
* @param code
* @param msg
* @param data
*/
public AjaxResult(int code, String msg, Object data)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if(data!=null){
super.put(DATA_TAG, data);
}
}
/**
*
*
* @return
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
}
/**
*
*
* @return
*/
public static AjaxResult success(Object data)
{
return AjaxResult.success("操作成功", data);
}
/**
*
*
* @param msg
* @return
*/
public static AjaxResult success(String msg)
{
return AjaxResult.success(msg, null);
}
/**
*
*
* @param msg
* @param data
* @return
*/
public static AjaxResult success(String msg, Object data)
{
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
*
*
* @param msg
* @return
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
}
/**
*
*
* @param msg
* @param data
* @return
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
*
*
* @return
*/
public static AjaxResult error()
{
return AjaxResult.error("操作失败");
}
/**
*
*
* @param msg
* @return
*/
public static AjaxResult error(String msg)
{
return AjaxResult.error(msg, null);
}
/**
*
*
* @param msg
* @param data
* @return
*/
public static AjaxResult error(String msg, Object data)
{
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
*
*
* @param code
* @param msg
* @return
*/
public static AjaxResult error(int code, String msg)
{
return new AjaxResult(code, msg, null);
}
/**
*
*
* @return
*/
public boolean isSuccess()
{
return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
}
/**
*
*
* @return
*/
public boolean isWarn()
{
return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
}
/**
*
*
* @return
*/
public boolean isError()
{
return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
}
/**
* 便
*
* @param key
* @param value
* @return
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
}

View File

@ -0,0 +1,33 @@
package com.bruce.sams.Utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Component;
@Component
public class PasswordEncoder {
// 从配置文件中读取 bcrypt 的强度默认强度为12用于生成盐值。
@Value("${bcrypt.strength:12}") // 如果没有设置则默认为12
private int bcryptStrength;
/**
*
* @param rawPassword
* @return
*/
public String encode(String rawPassword) {
return BCrypt.hashpw(rawPassword, BCrypt.gensalt(bcryptStrength));
}
/**
*
* @param rawPassword
* @param encodedPassword
* @return True False
*/
public Boolean matches(String rawPassword, String encodedPassword) {
return BCrypt.checkpw(rawPassword, encodedPassword);
}
}

View File

@ -0,0 +1,87 @@
package com.bruce.sams.Utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TokenUtil {
// 密钥用于签名和验证Token
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
// Token过期时间单位毫秒
private static final long EXPIRATION_TIME = 3600000; // 1小时
/**
* Token
*
* @param userId ID
* @return Token
*/
public static String generateToken(String userId) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId); // 将用户ID放入Token的负载部分
return Jwts.builder()
.setClaims(claims) // 设置自定义信息
.setIssuedAt(new Date(System.currentTimeMillis())) // 设置签发时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 设置过期时间
.signWith(SECRET_KEY) // 使用密钥进行签名
.compact(); // 生成Token
}
/**
* Token
*
* @param token Token
* @return
*/
public static boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(SECRET_KEY) // 设置密钥
.build()
.parseClaimsJws(token); // 解析Token
return true; // 如果没有异常表示Token有效
} catch (Exception e) {
return false; // 如果解析失败表示Token无效
}
}
/**
* TokenID
*
* @param token Token
* @return ID
*/
public static String getUserIdFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(SECRET_KEY) // 设置密钥
.build()
.parseClaimsJws(token) // 解析Token
.getBody();
return claims.get("userId", String.class); // 提取用户ID
}
/**
* Token
*
* @param token Token
* @return
*/
public static Date getExpirationDateFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(SECRET_KEY) // 设置密钥
.build()
.parseClaimsJws(token) // 解析Token
.getBody();
return claims.getExpiration(); // 获取Token的过期时间
}
}

View File

@ -0,0 +1,19 @@
package com.bruce.sams.mapper;
import com.bruce.sams.Entity.SysUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author bruce
* @description sys_user()Mapper
* @createDate 2025-02-08 00:19:50
* @Entity com.bruce.sams.Entity.SysUser
*/
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}

View File

@ -0,0 +1,11 @@
package com.bruce.sams.service;
public interface SysLoginService {
/**
*
* @param username
* @param password
* @return token
*/
public String login(String username, String password);
}

View File

@ -0,0 +1,20 @@
package com.bruce.sams.service;
import com.bruce.sams.Entity.SysUser;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author bruce
* @description sys_user()Service
* @createDate 2025-02-08 00:19:50
*/
public interface SysUserService extends IService<SysUser> {
/**
*
* @param userId ID
* @param newPassword
* @return T F
*/
public boolean changePassword(Long userId, String newPassword);
}

View File

@ -0,0 +1,49 @@
package com.bruce.sams.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bruce.sams.Entity.SysUser;
import com.bruce.sams.Exception.User.IncorrectPasswordException;
import com.bruce.sams.Exception.User.UserNotFoundException;
import com.bruce.sams.Utils.PasswordEncoder;
import com.bruce.sams.Utils.TokenUtil;
import com.bruce.sams.mapper.SysUserMapper;
import com.bruce.sams.service.SysLoginService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class SysLoginServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
implements SysLoginService {
@Resource
private PasswordEncoder passwordEncoder;
/**
*
* @param schoolId
* @param password
* @return Tokennull
*/
@Override
public String login(String schoolId, String password) {
// 查找用户
SysUser user = this.getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getSchoolId, schoolId));
//如果用户不存在,抛出用户未找到异常
if (user == null) {
throw new UserNotFoundException("用户不存在");
}
// 检查密码是否正确
if (!passwordEncoder.matches(password, user.getPasswd())) {
throw new IncorrectPasswordException("密码错误");
}
// 登录成功生成Token
// 返回Token
return TokenUtil.generateToken(user.getUserId().toString());
}
}

View File

@ -0,0 +1,71 @@
package com.bruce.sams.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bruce.sams.Entity.SysUser;
import com.bruce.sams.Utils.PasswordEncoder;
import com.bruce.sams.service.SysUserService;
import com.bruce.sams.mapper.SysUserMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
/**
* @author bruce
* @description sys_user()Service
* @createDate 2025-02-08 00:19:50
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
implements SysUserService {
@Resource
private PasswordEncoder passwordEncoder;
/**
*
* @param entity
* @return T F
*/
@Override
public boolean save(SysUser entity) {
// 如果用户输入了密码,则加密后保存
if (entity.getPasswd() != null) {
entity.setPasswd(passwordEncoder.encode(entity.getPasswd())); // 加密密码
}
return super.save(entity); // 调用父类方法保存用户
}
/**
* ID
* @param entity
* @return T F
*/
@Override
public boolean updateById(SysUser entity) {
// 更新时如果密码字段有变化,则加密新密码
if (entity.getPasswd() != null) {
entity.setPasswd(passwordEncoder.encode(entity.getPasswd())); // 加密新密码
}
return super.updateById(entity); // 调用父类方法更新用户信息
}
/**
*
* @param userId ID
* @param newPassword
* @return T F
*/
@Override
public boolean changePassword(Long userId, String newPassword) {
// 根据用户ID获取用户信息
SysUser user = this.getById(userId);
if (user == null) {
throw new RuntimeException("用户不存在"); // 如果用户不存在,抛出异常
}
// 加密新密码
String encodedPassword = passwordEncoder.encode(newPassword);
user.setPasswd(encodedPassword); // 设置加密后的新密码
return this.updateById(user); // 更新用户信息
}
}

View File

@ -1 +0,0 @@
spring.application.name=SAMS

View File

@ -0,0 +1,10 @@
spring:
application:
name: SAMS
datasource:
url: jdbc:mysql://localhost:3306/SAMS
username: root
password: admin123
driver-class-name: com.mysql.cj.jdbc.Driver

View File

@ -0,0 +1,30 @@
<?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.bruce.sams.mapper.SysUserMapper">
<resultMap id="BaseResultMap" type="com.bruce.sams.Entity.SysUser">
<id property="userId" column="user_id" />
<result property="roleId" column="role_id" />
<result property="nickName" column="nick_name" />
<result property="userName" column="user_name" />
<result property="passwd" column="passwd" />
<result property="schoolId" column="school_id" />
<result property="collegeId" column="college_id" />
<result property="email" column="email" />
<result property="phonenumber" column="phonenumber" />
<result property="sex" column="sex" />
<result property="avatar" column="avatar" />
<result property="status" column="status" />
<result property="remark" column="remark" />
</resultMap>
<sql id="Base_Column_List">
user_id,role_id,nick_name,user_name,passwd,school_id,
college_id,email,phonenumber,sex,avatar,
status,remark
</sql>
</mapper>