added: 学员登录api

This commit is contained in:
none 2023-03-10 14:10:55 +08:00
parent 06efa97e9f
commit f9dec16760
15 changed files with 446 additions and 101 deletions

View File

@ -8,7 +8,8 @@ public class SystemConstant {
public final static String REDIS_PREFIX = "playedu:";
public final static String JWT_PRV_ADMIN_USER = "dc14511e97e7eb725fb2976bc939b375";
public final static String JWT_PRV_ADMIN_USER = "dc14511e97e7eb725fb2976bc939b375";//AdminUser的md5加密
public final static String JWT_PRV_USER = "8f9bfe9d1345237cb3b2b205864da075";//User的md5加密
public final static String INTERNAL_IP = "127.0.0.1";

View File

@ -1,11 +1,63 @@
package xyz.playedu.api.controller.frontend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.domain.User;
import xyz.playedu.api.event.UserLoginEvent;
import xyz.playedu.api.request.frontend.LoginPasswordRequest;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.service.UserService;
import xyz.playedu.api.types.JsonResponse;
import xyz.playedu.api.types.JwtToken;
import xyz.playedu.api.util.HelperUtil;
import xyz.playedu.api.util.IpUtil;
import xyz.playedu.api.util.RequestUtil;
import java.util.Date;
import java.util.HashMap;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/2 21:51
*/
@RestController
@RequestMapping("/api/v1/auth/login")
public class LoginController {
@Autowired
private UserService userService;
@Autowired
private JWTService jwtService;
@Autowired
private ApplicationContext ctx;
@PostMapping("/password")
public JsonResponse password(@RequestBody @Validated LoginPasswordRequest req) {
User user = userService.find(req.getEmail());
if (user == null) {
return JsonResponse.error("邮箱未注册");
}
if (!HelperUtil.MD5(req.getPassword() + user.getSalt()).equals(user.getPassword())) {
return JsonResponse.error("密码错误");
}
JwtToken token = jwtService.generate(user.getId(), RequestUtil.url(), SystemConstant.JWT_PRV_USER);
HashMap<String, Object> data = new HashMap<>();
data.put("token", token.getToken());
data.put("expired", token.getExpire());
ctx.publishEvent(new UserLoginEvent(this, user.getId(), new Date(), token.getToken(), IpUtil.getIpAddress(), RequestUtil.ua()));
return JsonResponse.data(data);
}
}

View File

@ -0,0 +1,141 @@
package xyz.playedu.api.domain;
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 java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName user_login_records
*/
@TableName(value ="user_login_records")
@Data
public class UserLoginRecord implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private Integer userId;
/**
* JTI
*/
private String jti;
/**
* 登录ip
*/
private String ip;
/**
* Ip解析区域
*/
private String ipArea;
/**
* 浏览器
*/
private String browser;
/**
* 浏览器版本
*/
private String browserVersion;
/**
* 操作系统
*/
private String os;
/**
* 过期时间
*/
private Long expired;
/**
* 是否注销
*/
private Integer isLogout;
/**
* 创建时间
*/
private Date createdAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
UserLoginRecord other = (UserLoginRecord) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
&& (this.getJti() == null ? other.getJti() == null : this.getJti().equals(other.getJti()))
&& (this.getIp() == null ? other.getIp() == null : this.getIp().equals(other.getIp()))
&& (this.getIpArea() == null ? other.getIpArea() == null : this.getIpArea().equals(other.getIpArea()))
&& (this.getBrowser() == null ? other.getBrowser() == null : this.getBrowser().equals(other.getBrowser()))
&& (this.getBrowserVersion() == null ? other.getBrowserVersion() == null : this.getBrowserVersion().equals(other.getBrowserVersion()))
&& (this.getOs() == null ? other.getOs() == null : this.getOs().equals(other.getOs()))
&& (this.getExpired() == null ? other.getExpired() == null : this.getExpired().equals(other.getExpired()))
&& (this.getIsLogout() == null ? other.getIsLogout() == null : this.getIsLogout().equals(other.getIsLogout()))
&& (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
result = prime * result + ((getJti() == null) ? 0 : getJti().hashCode());
result = prime * result + ((getIp() == null) ? 0 : getIp().hashCode());
result = prime * result + ((getIpArea() == null) ? 0 : getIpArea().hashCode());
result = prime * result + ((getBrowser() == null) ? 0 : getBrowser().hashCode());
result = prime * result + ((getBrowserVersion() == null) ? 0 : getBrowserVersion().hashCode());
result = prime * result + ((getOs() == null) ? 0 : getOs().hashCode());
result = prime * result + ((getExpired() == null) ? 0 : getExpired().hashCode());
result = prime * result + ((getIsLogout() == null) ? 0 : getIsLogout().hashCode());
result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", userId=").append(userId);
sb.append(", jti=").append(jti);
sb.append(", ip=").append(ip);
sb.append(", ipArea=").append(ipArea);
sb.append(", browser=").append(browser);
sb.append(", browserVersion=").append(browserVersion);
sb.append(", os=").append(os);
sb.append(", expired=").append(expired);
sb.append(", isLogout=").append(isLogout);
sb.append(", createdAt=").append(createdAt);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,37 @@
package xyz.playedu.api.event;
import cn.hutool.http.useragent.UserAgent;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.util.Date;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/10 13:22
*/
@Setter
@Getter
public class UserLoginEvent extends ApplicationEvent {
private Integer userId;
private Date loginAt;
private String token;
private String ip;
private UserAgent userAgent;
public UserLoginEvent(Object source, Integer userId, Date loginAt, String token, String ip, UserAgent userAgent) {
super(source);
this.userId = userId;
this.loginAt = loginAt;
this.token = token;
this.ip = ip;
this.userAgent = userAgent;
}
}

View File

@ -0,0 +1,47 @@
package xyz.playedu.api.listener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import xyz.playedu.api.constant.SystemConstant;
import xyz.playedu.api.event.UserLoginEvent;
import xyz.playedu.api.exception.JwtLogoutException;
import xyz.playedu.api.service.JWTService;
import xyz.playedu.api.service.UserLoginRecordService;
import xyz.playedu.api.types.JWTPayload;
import xyz.playedu.api.util.IpUtil;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/10 13:45
*/
@Component
@Slf4j
public class UserLoginListener {
@Autowired
private UserLoginRecordService loginRecordService;
@Autowired
private JWTService jwtService;
@EventListener
@Async
public void updateLoginInfo(UserLoginEvent event) throws JwtLogoutException {
String ipArea = IpUtil.getRealAddressByIP(event.getIp());
JWTPayload payload = jwtService.parse(event.getToken(), SystemConstant.JWT_PRV_USER);
loginRecordService.store(
event.getUserId(),
payload.getJti(),
payload.getExp(),
event.getIp(),
ipArea,
event.getUserAgent().getBrowser().toString(),
event.getUserAgent().getVersion(),
event.getUserAgent().getOs().toString()
);
}
}

View File

@ -0,0 +1,20 @@
package xyz.playedu.api.mapper;
import org.apache.ibatis.annotations.Mapper;
import xyz.playedu.api.domain.UserLoginRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author tengteng
* @description 针对表user_login_records的数据库操作Mapper
* @createDate 2023-03-10 14:06:55
* @Entity xyz.playedu.api.domain.UserLoginRecord
*/
@Mapper
public interface UserLoginRecordMapper extends BaseMapper<UserLoginRecord> {
}

View File

@ -1,5 +1,6 @@
package xyz.playedu.api.request.backend;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.HashMap;
@ -11,6 +12,7 @@ import java.util.HashMap;
@Data
public class AppConfigRequest {
@NotNull(message = "配置为空")
private HashMap<String, String> data;
}

View File

@ -0,0 +1,28 @@
package xyz.playedu.api.request.frontend;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/10 13:13
*/
@Data
public class LoginPasswordRequest {
@NotBlank(message = "请输入邮箱")
private String email;
@NotBlank(message = "请输入密码")
private String password;
@NotBlank(message = "请输入验证码")
@JsonProperty("captcha_key")
private String captchaKey;
@NotBlank(message = "请输入验证码")
@JsonProperty("captcha_val")
private String captchaVal;
}

View File

@ -0,0 +1,15 @@
package xyz.playedu.api.service;
import xyz.playedu.api.domain.UserLoginRecord;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author tengteng
* @description 针对表user_login_records的数据库操作Service
* @createDate 2023-03-10 13:40:33
*/
public interface UserLoginRecordService extends IService<UserLoginRecord> {
UserLoginRecord store(Integer userId, String jti, Long expired, String ip, String ipArea, String browser, String browserVersion, String os);
void saveIpArea(Integer id, String area);
}

View File

@ -24,6 +24,10 @@ public interface UserService extends IService<User> {
User findOrFail(Integer id) throws NotFoundException;
User find(Integer id);
User find(String email);
User createWithDepIds(String email, String name, String avatar, String password, String idCard, Integer[] depIds);
User updateWithDepIds(User user, String email, String nickname, String name, String avatar, String password, String idCard, Integer[] depIds);

View File

@ -0,0 +1,43 @@
package xyz.playedu.api.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xyz.playedu.api.domain.UserLoginRecord;
import xyz.playedu.api.service.UserLoginRecordService;
import xyz.playedu.api.mapper.UserLoginRecordMapper;
import org.springframework.stereotype.Service;
/**
* @author tengteng
* @description 针对表user_login_records的数据库操作Service实现
* @createDate 2023-03-10 13:40:33
*/
@Service
public class UserLoginRecordServiceImpl extends ServiceImpl<UserLoginRecordMapper, UserLoginRecord>
implements UserLoginRecordService {
@Override
public UserLoginRecord store(Integer userId, String jti, Long expired, String ip, String ipArea, String browser, String browserVersion, String os) {
UserLoginRecord record = new UserLoginRecord();
record.setUserId(userId);
record.setJti(jti);
record.setExpired(expired);
record.setIp(ip);
record.setIpArea(ipArea);
record.setBrowser(browser);
record.setBrowserVersion(browserVersion);
record.setOs(os);
save(record);
return record;
}
@Override
public void saveIpArea(Integer id, String area) {
UserLoginRecord record = new UserLoginRecord();
record.setId(id);
record.setIpArea(area);
updateById(record);
}
}

View File

@ -193,6 +193,16 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
public List<Integer> getDepIdsByUserId(Integer userId) {
return userDepartmentService.list(userDepartmentService.query().getWrapper().eq("user_id", userId)).stream().map(UserDepartment::getDepId).toList();
}
@Override
public User find(Integer id) {
return getOne(query().getWrapper().eq("id", id));
}
@Override
public User find(String email) {
return getOne(query().getWrapper().eq("email", email));
}
}

View File

@ -3,8 +3,7 @@ package xyz.playedu.api.util;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import jakarta.servlet.http.HttpServletRequest;
@ -12,10 +11,9 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
@Slf4j
public class IpUtil {
private static final Logger log = LoggerFactory.getLogger(IpUtil.class);
/**
* 获取客户端IP
*
@ -218,20 +216,6 @@ public class IpUtil {
return "127.0.0.1";
}
/**
* 获取主机名
*
* @return 本地主机名
* @author fzr
*/
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException ignored) {
}
return "未知";
}
/**
* 从多级反向代理中获得第一个非unknown IP地址
*

View File

@ -1,5 +1,7 @@
package xyz.playedu.api.util;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -9,18 +11,17 @@ import java.util.List;
public class RequestUtil {
/**
* 获取请求对象
*
* @return HttpServletRequest
* @author fzr
*/
public static HttpServletRequest handler() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (servletRequestAttributes != null) {
return servletRequestAttributes.getRequest();
return servletRequestAttributes == null ? null : servletRequestAttributes.getRequest();
}
public static UserAgent ua() {
HttpServletRequest request = RequestUtil.handler();
if (request == null) {
return null;
}
return null;
return UserAgentUtil.parse(request.getHeader("User-Agent"));
}
public static String token() {
@ -35,65 +36,24 @@ public class RequestUtil {
return token.split(" ")[1];
}
/**
* 获取不带参请求URl
* 示例: https://127.0.0.1:8082/api/system/menu/menus
*
* @return String
* @author fzr
*/
public static String url() {
HttpServletRequest request = RequestUtil.handler();
if (request != null) {
return request.getRequestURL().toString();
}
return "";
return request == null ? "" : request.getRequestURL().toString();
}
/**
* 获取带端口的请求地址
* 示例: https://127.0.0.1:8082
*
* @return String
* @author fzr
*/
public static String uri() {
String domain = RequestUtil.domain();
if (!Arrays.asList(443, 80, 0).contains(RequestUtil.port())) {
domain += ":" + RequestUtil.port();
}
return domain;
Integer portNumber = port();
return RequestUtil.domain() + (Arrays.asList(443, 80, 0).contains(portNumber) ? "" : ":" + portNumber);
}
/**
* 获取请求路由
* 示例: /api/system/menu/menus
*
* @return String
* @author fzr
*/
public static String route() {
public static String pathname() {
HttpServletRequest request = RequestUtil.handler();
if (request != null) {
return request.getRequestURI();
}
return "";
return request == null ? "" : request.getRequestURI();
}
/**
* 获取请求端口
* 示例: 443/80
*
* @return Integer
* @author fzr
*/
public static Integer port() {
HttpServletRequest request = RequestUtil.handler();
if (request != null) {
return request.getServerPort();
}
return 0;
return request == null ? 0 : request.getServerPort();
}
public static String domain() {
@ -106,30 +66,4 @@ public class RequestUtil {
}
return null;
}
public static Boolean isGet() {
return isMethod("GET");
}
public static Boolean isPost() {
return isMethod("POST");
}
public static Boolean isPUT() {
return isMethod("PUT");
}
public static Boolean isDelete() {
return isMethod("DELETE");
}
public static boolean isMethod(String method) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (servletRequestAttributes != null) {
HttpServletRequest request = servletRequestAttributes.getRequest();
return request.getMethod().equals(method);
}
return false;
}
}

View File

@ -0,0 +1,27 @@
<?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="xyz.playedu.api.mapper.UserLoginRecordMapper">
<resultMap id="BaseResultMap" type="xyz.playedu.api.domain.UserLoginRecord">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userId" column="user_id" jdbcType="INTEGER"/>
<result property="jti" column="jti" jdbcType="VARCHAR"/>
<result property="ip" column="ip" jdbcType="VARCHAR"/>
<result property="ipArea" column="ip_area" jdbcType="VARCHAR"/>
<result property="browser" column="browser" jdbcType="VARCHAR"/>
<result property="browserVersion" column="browser_version" jdbcType="VARCHAR"/>
<result property="os" column="os" jdbcType="VARCHAR"/>
<result property="expired" column="expired" jdbcType="BIGINT"/>
<result property="isLogout" column="is_logout" jdbcType="TINYINT"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,user_id,jti,
ip,ip_area,browser,
browser_version,os,expired,
is_logout,created_at
</sql>
</mapper>