配置安全模块与邮件登陆模组
This commit is contained in:
parent
e6d88afd79
commit
45c0ad0da0
1
.gitignore
vendored
1
.gitignore
vendored
@ -34,3 +34,4 @@ build/
|
||||
|
||||
### 自定义 ###
|
||||
*.pdf
|
||||
/src/main/resources/application-dev.yml
|
||||
|
@ -195,7 +195,7 @@ CREATE TABLE `oa_user`
|
||||
`account_no_expired` tinyint(1) NOT NULL DEFAULT '1' COMMENT '账户是否过期',
|
||||
`credentials_no_expired` tinyint(1) NOT NULL DEFAULT '0' COMMENT '密码是否过期',
|
||||
`recommend` tinyint(1) NOT NULL DEFAULT '0' COMMENT '账户是否被推荐',
|
||||
`account_no_locked` tinyint(1) NOT NULL COMMENT '账户是否被锁定',
|
||||
`account_no_locked` tinyint(1) NOT NULL DEFAULT '1' COMMENT '账户是否被锁定',
|
||||
`description` text COMMENT '个人简介',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间'
|
||||
|
16
pom.xml
16
pom.xml
@ -103,6 +103,22 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>24.1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- SpringBoot Mail -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<!-- Gson -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
81
src/main/java/com/jsl/oa/config/JwtFilter.java
Normal file
81
src/main/java/com/jsl/oa/config/JwtFilter.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.jsl.oa.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.JwtUtil;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* <h1>JWT过滤器</h1>
|
||||
* <hr/>
|
||||
* 用于JWT的过滤器
|
||||
*
|
||||
* @since v1.1.0
|
||||
* @version v1.1.0
|
||||
* @author 筱锋xiao_lfeng
|
||||
*/
|
||||
public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
|
||||
/**
|
||||
* <h2>判断用户Token</h2>
|
||||
* <hr/>
|
||||
* 判断用户Token是否存在,如果存在则进行验证
|
||||
*
|
||||
* @param request 请求
|
||||
* @param response 响应
|
||||
* @param mappedValue 映射值
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
// 判断是否存在Authorization Header
|
||||
String token = getAuthzHeader(request);
|
||||
if (token == null || token.isEmpty()) {
|
||||
return false; // 未提供Token,拒绝访问
|
||||
} else {
|
||||
// 解析Bearer后面的令牌
|
||||
token = token.replace("Bearer ", "");
|
||||
System.out.println(token);
|
||||
return JwtUtil.verify(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>访问被拒绝时</h2>
|
||||
* <hr/>
|
||||
* 当访问被拒绝时,会调用此方法
|
||||
*
|
||||
* @param request 请求
|
||||
* @param response 响应
|
||||
* @param mappedValue 映射值
|
||||
* @return {@link Boolean}
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest request, @NotNull ServletResponse response, Object mappedValue) throws Exception {
|
||||
Gson gson = new Gson();
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().println(gson.toJson(ResultUtil.error(ErrorCode.UNAUTHORIZED)));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>获取Authorization Header</h2>
|
||||
* <hr/>
|
||||
* 用于获取Authorization Header
|
||||
*
|
||||
* @param request 请求
|
||||
* @return {@link String}
|
||||
*/
|
||||
@Override
|
||||
protected String getAuthzHeader(ServletRequest request) {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
return httpRequest.getHeader("Authorization");
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.jsl.oa.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class JwtToken implements AuthenticationToken {
|
||||
|
||||
private final String token;
|
||||
private final String username;
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return token;
|
||||
}
|
||||
}
|
47
src/main/java/com/jsl/oa/config/MailConfiguration.java
Normal file
47
src/main/java/com/jsl/oa/config/MailConfiguration.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.jsl.oa.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* <h1>邮件配置类</h1>
|
||||
* <hr/>
|
||||
* 用于配置邮件发送相关信息
|
||||
*
|
||||
* @since v1.1.0
|
||||
* @version v1.1.0
|
||||
* @author 筱锋xiao_lfeng
|
||||
*/
|
||||
@Configuration
|
||||
public class MailConfiguration {
|
||||
|
||||
@Value("${spring.mail.host}")
|
||||
private String emailHost;
|
||||
@Value("${spring.mail.username}")
|
||||
private String emailUsername;
|
||||
@Value("${spring.mail.password}")
|
||||
private String emailPassword;
|
||||
|
||||
@Bean
|
||||
public JavaMailSender javaMailSender() {
|
||||
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
|
||||
mailSender.setDefaultEncoding("UTF-8");
|
||||
mailSender.setHost(emailHost);
|
||||
mailSender.setPort(25); // 你的邮件服务器端口
|
||||
mailSender.setUsername(emailUsername);
|
||||
mailSender.setPassword(emailPassword);
|
||||
|
||||
Properties props = mailSender.getJavaMailProperties();
|
||||
props.put("mail.transport.protocol", "smtp");
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", "true");
|
||||
props.put("mail.debug", "true");
|
||||
|
||||
return mailSender;
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package com.jsl.oa.config;
|
||||
|
||||
import com.jsl.oa.model.doData.UserDO;
|
||||
import com.jsl.oa.services.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.shiro.authc.*;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MyRealm extends AuthorizingRealm {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
/**
|
||||
* 授权
|
||||
* @param principalCollection 令牌
|
||||
* @return 授权信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证
|
||||
* @param authenticationToken 令牌
|
||||
* @return 认证信息
|
||||
* @throws AuthenticationException 认证异常
|
||||
*/
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
|
||||
JwtToken jwtToken = (JwtToken) authenticationToken;
|
||||
String username = jwtToken.getUsername();
|
||||
|
||||
// 从数据库获取用户信息
|
||||
UserDO userDO = userService.getUserInfoByUsername(username);
|
||||
if (userDO == null) {
|
||||
throw new UnknownAccountException("用户不存在");
|
||||
} else if (!userDO.getAccountNoLocked()) {
|
||||
throw new LockedAccountException("用户已被锁定");
|
||||
} else if (!userDO.getEnabled()) {
|
||||
throw new DisabledAccountException("用户已被禁用");
|
||||
} else if (!userDO.getAccountNoExpired()) {
|
||||
throw new ExpiredCredentialsException("用户已过期");
|
||||
}
|
||||
|
||||
return new SimpleAuthenticationInfo(username, jwtToken.getCredentials(), getName());
|
||||
}
|
||||
}
|
40
src/main/java/com/jsl/oa/config/shiro/MyRealm.java
Normal file
40
src/main/java/com/jsl/oa/config/shiro/MyRealm.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.jsl.oa.config.shiro;
|
||||
|
||||
import com.jsl.oa.services.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MyRealm extends AuthorizingRealm {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
/**
|
||||
* 授权
|
||||
*
|
||||
* @return 授权信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(@NotNull PrincipalCollection principals) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 认证
|
||||
*
|
||||
* @param authenticationToken 令牌
|
||||
* @return 认证信息
|
||||
* @throws AuthenticationException 认证异常
|
||||
*/
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.jsl.oa.config;
|
||||
package com.jsl.oa.config.shiro;
|
||||
|
||||
import com.jsl.oa.config.JwtFilter;
|
||||
import com.jsl.oa.services.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
@ -7,6 +8,7 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -24,11 +26,19 @@ public class ShiroConfiguration {
|
||||
// 配置过滤器规则
|
||||
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
|
||||
filterChainDefinitionMap.put("/auth/**", "anon"); // 登录接口允许匿名访问
|
||||
filterChainDefinitionMap.put("/unauthorized", "anon"); // 未授权接口允许匿名访问
|
||||
filterChainDefinitionMap.put("/", "anon"); // 首页允许匿名访问
|
||||
filterChainDefinitionMap.put("/**/**", "jwt"); // 其他接口一律拦截(需要Token)
|
||||
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
|
||||
|
||||
// 设置登录接口
|
||||
// 设置未登陆响应接口
|
||||
shiroFilterFactoryBean.setLoginUrl("/unauthorized");
|
||||
|
||||
// 添加JWT过滤器
|
||||
Map<String, Filter> filters = new LinkedHashMap<>();
|
||||
filters.put("jwt", new JwtFilter()); // 配置自定义的JWT过滤器
|
||||
shiroFilterFactoryBean.setFilters(filters);
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
@ -8,18 +8,19 @@ import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.Processing;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <h1>用户认证控制器</h1>
|
||||
* <hr/>
|
||||
* 用户认证控制器,包含用户注册、用户登录、用户登出接口
|
||||
*
|
||||
* @since v1.0.0
|
||||
* @version v1.1.0
|
||||
* @see AuthService
|
||||
* @see UserRegisterVO
|
||||
@ -28,6 +29,7 @@ import java.text.ParseException;
|
||||
* @see ErrorCode
|
||||
* @see Processing
|
||||
* @see ResultUtil
|
||||
* @since v1.0.0
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@ -39,12 +41,12 @@ public class AuthController {
|
||||
* <hr/>
|
||||
* 用户注册接口
|
||||
*
|
||||
* @since v1.0.0
|
||||
* @return {@link BaseResponse}
|
||||
* @author 筱锋xiao_lfeng
|
||||
* @since v1.0.0
|
||||
*/
|
||||
@PostMapping("/auth/register")
|
||||
public BaseResponse authRegister(@RequestBody @Validated UserRegisterVO userRegisterVO, BindingResult bindingResult) throws ParseException {
|
||||
public BaseResponse authRegister(@RequestBody @Validated UserRegisterVO userRegisterVO, @NotNull BindingResult bindingResult) throws ParseException {
|
||||
// 判断是否有参数错误
|
||||
if (bindingResult.hasErrors()) {
|
||||
return ResultUtil.error(ErrorCode.REQUEST_BODY_ERROR, Processing.getValidatedErrorList(bindingResult));
|
||||
@ -57,13 +59,14 @@ public class AuthController {
|
||||
* <hr/>
|
||||
* 用户登录接口
|
||||
*
|
||||
* @since v1.0.0
|
||||
* @param userLoginVO 用户登录信息
|
||||
* @param bindingResult 参数校验结果
|
||||
* @return {@link BaseResponse}
|
||||
* @author 176yunxuan
|
||||
* @since v1.0.0
|
||||
*/
|
||||
@GetMapping("/auth/login")
|
||||
public BaseResponse authLogin(@RequestBody @Validated UserLoginVO userLoginVO, BindingResult bindingResult){
|
||||
public BaseResponse authLogin(@RequestBody @Validated UserLoginVO userLoginVO, @NotNull BindingResult bindingResult) {
|
||||
// 判断是否有参数错误
|
||||
if (bindingResult.hasErrors()) {
|
||||
return ResultUtil.error(ErrorCode.REQUEST_BODY_ERROR, Processing.getValidatedErrorList(bindingResult));
|
||||
@ -71,6 +74,38 @@ public class AuthController {
|
||||
return authService.authLogin(userLoginVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>用户邮箱登录</h2>
|
||||
* <hr/>
|
||||
* 用户邮箱登录接口
|
||||
*
|
||||
* @param email 用户登陆邮箱
|
||||
* @return {@link BaseResponse}
|
||||
* @author 筱锋xiao_lfeng
|
||||
* @since v1.1.0
|
||||
*/
|
||||
@GetMapping("/auth/login/email")
|
||||
public BaseResponse authLoginByEmail(@RequestParam String email) {
|
||||
if (email != null) {
|
||||
if (Pattern.matches("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", email)) {
|
||||
return authService.authLoginByEmail(email);
|
||||
} else {
|
||||
return ResultUtil.error(ErrorCode.PARAMETER_ERROR);
|
||||
}
|
||||
} else {
|
||||
return ResultUtil.error(ErrorCode.PARAMETER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>用户登出</h2>
|
||||
* <hr/>
|
||||
* 用户登出接口
|
||||
*
|
||||
* @return {@link BaseResponse}
|
||||
* @since v1.1.0
|
||||
*/
|
||||
@GetMapping("/auth/logout")
|
||||
public BaseResponse authLogout() {
|
||||
return null;
|
||||
}
|
||||
|
@ -1,15 +1,20 @@
|
||||
package com.jsl.oa.controllers;
|
||||
|
||||
import com.jsl.oa.services.MailService;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class CustomController implements ErrorController {
|
||||
|
||||
private final MailService mailService;
|
||||
|
||||
@RequestMapping("/")
|
||||
public BaseResponse index() {
|
||||
return ResultUtil.success("欢迎使用JSL-OA系统,服务器处于正常状态");
|
||||
|
@ -1,21 +1,8 @@
|
||||
package com.jsl.oa.controllers;
|
||||
|
||||
import com.jsl.oa.model.voData.UserLoginVO;
|
||||
import com.jsl.oa.model.voData.UserRegisterVO;
|
||||
import com.jsl.oa.services.AuthService;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.Processing;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.BindingResult;
|
||||
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.RestController;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class InfoController {
|
||||
|
@ -1,21 +1,8 @@
|
||||
package com.jsl.oa.controllers;
|
||||
|
||||
import com.jsl.oa.model.voData.UserLoginVO;
|
||||
import com.jsl.oa.model.voData.UserRegisterVO;
|
||||
import com.jsl.oa.services.AuthService;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.Processing;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.BindingResult;
|
||||
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.RestController;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionController {
|
||||
|
@ -1,21 +1,8 @@
|
||||
package com.jsl.oa.controllers;
|
||||
|
||||
import com.jsl.oa.model.voData.UserLoginVO;
|
||||
import com.jsl.oa.model.voData.UserRegisterVO;
|
||||
import com.jsl.oa.services.AuthService;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.Processing;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.BindingResult;
|
||||
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.RestController;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class ProjectController {
|
||||
|
@ -1,21 +1,8 @@
|
||||
package com.jsl.oa.controllers;
|
||||
|
||||
import com.jsl.oa.model.voData.UserLoginVO;
|
||||
import com.jsl.oa.model.voData.UserRegisterVO;
|
||||
import com.jsl.oa.services.AuthService;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.Processing;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.BindingResult;
|
||||
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.RestController;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
|
||||
|
@ -13,6 +13,15 @@ public class UserDAO {
|
||||
|
||||
private final UserMapper userMapper;
|
||||
|
||||
/**
|
||||
* <h2>用户名获取用户信息</h2>
|
||||
* <hr/>
|
||||
* 根据用户名获取用户信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @author 筱锋xiao_lfeng
|
||||
* @return {@link UserDO}
|
||||
*/
|
||||
public UserDO getUserInfoByUsername(String username) {
|
||||
UserDO userDO = null;
|
||||
// 从 Redis 获取数据
|
||||
|
@ -37,4 +37,7 @@ public interface UserMapper {
|
||||
|
||||
@Select("select * from organize_oa.oa_user where id = #{id}")
|
||||
UserDO getUserById(Long id);
|
||||
|
||||
@Select("select * from organize_oa.oa_user where email = #{email}")
|
||||
UserDO getUserInfoByEmail(String email);
|
||||
}
|
||||
|
@ -16,8 +16,9 @@ import javax.validation.constraints.Pattern;
|
||||
*/
|
||||
@Getter
|
||||
public class UserLoginVO {
|
||||
@Pattern(regexp = "^[0-9A-Z]+$", message = "工号格式错误")
|
||||
private String jobId;
|
||||
@Pattern(regexp = "^[0-9A-Za-z_]+$", message = "支持用户名/手机号/工号登陆")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String user;
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.jsl.oa.model.voData;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* <h1>用户注册自定义实体类</h1>
|
||||
@ -12,29 +12,34 @@ import javax.validation.constraints.Pattern;
|
||||
*
|
||||
* @author 筱锋xiao_lfeng
|
||||
* @version v1.0.0
|
||||
* @since v1.0.0
|
||||
* @since v1.1.0
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserRegisterVO {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Pattern(regexp = "^[\\u4e00-\\u9fa5]{2,5}$", message = "用户名只能为字母、数字或下划线")
|
||||
@Pattern(regexp = "^[0-9A-Za-z_]{3,40}$", message = "用户名只能为字母、数字或下划线")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
@Pattern(regexp = "^[012]$", message = "保密:0,男:1,女:2")
|
||||
@NotBlank(message = "家乡不能为空")
|
||||
private String address;
|
||||
|
||||
@NotBlank(message = "电话不能为空")
|
||||
@Pattern(regexp = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$", message = "电话格式错误")
|
||||
private String phone;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", message = "邮箱格式错误")
|
||||
private String email;
|
||||
|
||||
@Min(value = 0, message = "保密:0,男:1,女:2")
|
||||
@Max(value = 2, message = "保密:0,男:1,女:2")
|
||||
@NotNull(message = "性别不能为空")
|
||||
private Short sex;
|
||||
|
||||
@NotBlank(message = "年龄不能为空")
|
||||
@NotNull(message = "年龄不能为空")
|
||||
private Short age;
|
||||
|
||||
@NotBlank(message = "单位不能为空")
|
||||
private String unit;
|
||||
|
||||
@NotBlank(message = "职位/专业不能为空")
|
||||
private String filed;
|
||||
|
||||
@NotBlank(message = "家乡不能为空")
|
||||
private String hometown;
|
||||
}
|
||||
|
28
src/main/java/com/jsl/oa/model/voData/UserReturnBackVO.java
Normal file
28
src/main/java/com/jsl/oa/model/voData/UserReturnBackVO.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.jsl.oa.model.voData;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* <h1>用户注册成功UserDO自定义实体类</h1>
|
||||
* <hr/>
|
||||
* 用于处理用户注册表单输出的数据
|
||||
*
|
||||
* @author 筱锋xiao_lfeng
|
||||
* @version v1.1.0
|
||||
* @since v1.1.0
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class UserReturnBackVO {
|
||||
private String jobId;
|
||||
private String username;
|
||||
private String address;
|
||||
private String phone;
|
||||
private String email;
|
||||
private Short age;
|
||||
private Short sex;
|
||||
private String token;
|
||||
}
|
@ -4,9 +4,45 @@ import com.jsl.oa.model.voData.UserLoginVO;
|
||||
import com.jsl.oa.model.voData.UserRegisterVO;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* <h1>用户认证服务接口</h1>
|
||||
* <hr/>
|
||||
* 用户认证服务接口,包含用户注册、用户登录、用户登出接口
|
||||
*
|
||||
* @version v1.1.0
|
||||
* @since v1.0.0
|
||||
*/
|
||||
public interface AuthService {
|
||||
BaseResponse authRegister(UserRegisterVO userRegisterVO) throws ParseException;
|
||||
/**
|
||||
* <h2>用户注册</h2>
|
||||
* <hr/>
|
||||
* 用户注册服务类操作
|
||||
*
|
||||
* @param userRegisterVO 用户注册信息
|
||||
* @return {@link BaseResponse}
|
||||
* @author 筱锋xiao_lfeng
|
||||
*/
|
||||
BaseResponse authRegister(UserRegisterVO userRegisterVO);
|
||||
|
||||
/**
|
||||
* <h2>用户登录</h2>
|
||||
* <hr/>
|
||||
* 用户登录服务类操作
|
||||
*
|
||||
* @param userLoginVO 用户登录信息
|
||||
* @return {@link BaseResponse}
|
||||
* @author 176yunxuan
|
||||
*/
|
||||
BaseResponse authLogin(UserLoginVO userLoginVO);
|
||||
|
||||
/**
|
||||
* <h2>用户登出</h2>
|
||||
* <hr/>
|
||||
* 用户登出服务类操作
|
||||
*
|
||||
* @param email 用户邮箱
|
||||
* @return {@link BaseResponse}
|
||||
* @author 筱锋xiao_lfeng
|
||||
*/
|
||||
BaseResponse authLoginByEmail(String email);
|
||||
}
|
||||
|
8
src/main/java/com/jsl/oa/services/MailService.java
Normal file
8
src/main/java/com/jsl/oa/services/MailService.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.jsl.oa.services;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
public interface MailService {
|
||||
void sendMail() throws MessagingException;
|
||||
}
|
||||
|
@ -2,57 +2,55 @@ package com.jsl.oa.services.impl;
|
||||
|
||||
import com.jsl.oa.model.doData.UserDO;
|
||||
import com.jsl.oa.model.voData.UserLoginVO;
|
||||
import com.jsl.oa.model.voData.UserReturnBackVO;
|
||||
import com.jsl.oa.model.voData.UserRegisterVO;
|
||||
import com.jsl.oa.exception.BusinessException;
|
||||
import com.jsl.oa.mapper.UserMapper;
|
||||
import com.jsl.oa.services.AuthService;
|
||||
import com.jsl.oa.utils.BaseResponse;
|
||||
import com.jsl.oa.utils.ErrorCode;
|
||||
import com.jsl.oa.utils.Processing;
|
||||
import com.jsl.oa.utils.ResultUtil;
|
||||
import com.jsl.oa.utils.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.mindrot.jbcrypt.BCrypt;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* <h1>用户认证服务实现类</h1>
|
||||
* <hr/>
|
||||
* 用户认证服务实现类,包含用户注册、用户登录、用户登出接口
|
||||
*
|
||||
* @since v1.0.0
|
||||
* @version v1.1.0
|
||||
* @see AuthService
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AuthServiceImpl implements AuthService {
|
||||
private final UserMapper userMapper;
|
||||
|
||||
/**
|
||||
* <h1>用户注册</h1>
|
||||
* <hr/>
|
||||
* 用户注册服务类操作
|
||||
*
|
||||
* @param userRegisterVO 用户注册信息
|
||||
* @return {@link BaseResponse}
|
||||
* @throws ParseException 日期转换异常
|
||||
*/
|
||||
@Override
|
||||
public BaseResponse authRegister(UserRegisterVO userRegisterVO) {
|
||||
// 用户检查是否存在
|
||||
// 检查用户说是否存在
|
||||
UserDO getUserByUsername = userMapper.getUserInfoByUsername(userRegisterVO.getUsername());
|
||||
// 用户名已存在
|
||||
if (getUserByUsername != null) {
|
||||
return ResultUtil.error(ErrorCode.USER_EXIST);
|
||||
}
|
||||
|
||||
// 生成工号
|
||||
String userNum;
|
||||
do {
|
||||
userNum = Processing.createJobNumber((short) 2);
|
||||
} while (userMapper.getUserByUserNum(userNum) != null);
|
||||
// 处理性别
|
||||
|
||||
// 数据上传
|
||||
UserDO userDO = new UserDO();
|
||||
userDO.setJobId(userNum)
|
||||
.setUsername(userRegisterVO.getUsername())
|
||||
.setPassword(BCrypt.hashpw(userRegisterVO.getPassword(), BCrypt.gensalt()))
|
||||
.setSex(userRegisterVO.getSex())
|
||||
.setAge(userRegisterVO.getAge());
|
||||
.setAddress(userRegisterVO.getAddress())
|
||||
.setPhone(userRegisterVO.getPhone())
|
||||
.setEmail(userRegisterVO.getEmail())
|
||||
.setAge(userRegisterVO.getAge())
|
||||
.setSex(userRegisterVO.getSex());
|
||||
// 插入数据
|
||||
if (userMapper.insertUser(userDO)) {
|
||||
userDO.setPassword(null);
|
||||
@ -63,15 +61,31 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseResponse authLogin(UserLoginVO userLoginVO) {
|
||||
String pwd = userLoginVO.getPassword();
|
||||
String encodePwd = userMapper.loginPassword(userLoginVO);
|
||||
if (encodePwd == null) {
|
||||
public BaseResponse authLogin(@NotNull UserLoginVO userLoginVO) {
|
||||
// 检查用户是否存在
|
||||
UserDO userDO = userMapper.getUserInfoByUsername(userLoginVO.getUser());
|
||||
if (userDO != null) {
|
||||
// 获取用户并登陆
|
||||
if (BCrypt.checkpw(userLoginVO.getPassword(), userDO.getPassword())) {
|
||||
UserReturnBackVO userReturnBackVO = new UserReturnBackVO();
|
||||
// 授权 Token
|
||||
String token = JwtUtil.generateToken(userDO.getUsername());
|
||||
userReturnBackVO.setAddress(userDO.getAddress())
|
||||
.setAge(userDO.getAge())
|
||||
.setEmail(userDO.getEmail())
|
||||
.setJobId(userDO.getJobId())
|
||||
.setPhone(userDO.getPhone())
|
||||
.setSex(userDO.getSex())
|
||||
.setUsername(userDO.getUsername())
|
||||
.setToken(token);
|
||||
return ResultUtil.success("登陆成功", userReturnBackVO);
|
||||
} else {
|
||||
return ResultUtil.error(ErrorCode.WRONG_PASSWORD);
|
||||
}
|
||||
} else {
|
||||
return ResultUtil.error(ErrorCode.USER_NOT_EXIST);
|
||||
}
|
||||
if (BCrypt.checkpw(pwd, encodePwd)) {
|
||||
return ResultUtil.success("登陆成功", userMapper.login(userLoginVO));
|
||||
} else return ResultUtil.error(ErrorCode.WRONG_PASSWORD);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
47
src/main/java/com/jsl/oa/services/impl/MailServiceImpl.java
Normal file
47
src/main/java/com/jsl/oa/services/impl/MailServiceImpl.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.jsl.oa.services.impl;
|
||||
|
||||
import com.jsl.oa.services.MailService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MailServiceImpl implements MailService {
|
||||
private final JavaMailSender javaMailSender;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String from;
|
||||
|
||||
|
||||
@Override
|
||||
public void sendMail() {
|
||||
//发送多媒体邮件
|
||||
try {
|
||||
MimeMessage message = javaMailSender.createMimeMessage();
|
||||
|
||||
//第二个参数控制着附件上传
|
||||
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||||
|
||||
helper.setFrom(from);
|
||||
String to = "lfengzeng@vip.qq.com";
|
||||
helper.setTo(to);
|
||||
String subject = "springboot测试邮件";
|
||||
helper.setSubject(subject);
|
||||
|
||||
//第二个参数表示以 html 语法解析文本
|
||||
String text = "你好:这是一封测试邮件。请坚持下去。加油!";
|
||||
helper.setText(text, true);
|
||||
|
||||
javaMailSender.send(message);
|
||||
|
||||
} catch (Exception e) {
|
||||
//TODO: 10001-发送邮件失败处理
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ public enum ErrorCode {
|
||||
USER_EXIST("UserExist", 40013, "用户名已存在"),
|
||||
TIMESTAMP_ERROR("TimestampError", 40014, "时间戳错误"),
|
||||
USER_NOT_EXIST("UserNotExist", 40015, "用户不存在"),
|
||||
UNAUTHORIZED("Unauthorized", 40100, "未授权"),
|
||||
DATABASE_INSERT_ERROR("DatabaseInsertError", 50010, "数据库插入错误"),
|
||||
DATABASE_UPDATE_ERROR("DatabaseUpdateError", 50011, "数据库更新错误"),
|
||||
DATABASE_DELETE_ERROR("DatabaseDeleteError", 50012, "数据库删除错误");
|
||||
|
@ -7,10 +7,11 @@ import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JwtUtil {
|
||||
// 替换为实际的密钥,建议使用足够长的随机字符串
|
||||
private static final String SECRET_KEY = Processing.createJobNumber((short) 1, (short) 255);
|
||||
private static final String SECRET_KEY = "238542310128901753637022851772455105464283332917211091531086967815273100806759714250034263888525489008903447113697698540563820710887668094087054975808574632265678643370464260078072153369247242449569221118098938297741582538222826493707667477115117609126233";
|
||||
|
||||
// Token 有效期,这里设置为一天,可以根据实际需求调整
|
||||
private static final long EXPIRATION_TIME = 86400000;
|
||||
@ -27,7 +28,7 @@ public class JwtUtil {
|
||||
}
|
||||
|
||||
// 验证Token
|
||||
public static boolean verify(String token, String username) {
|
||||
public static boolean verify(String token) {
|
||||
try {
|
||||
Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
|
||||
|
||||
@ -38,9 +39,8 @@ public class JwtUtil {
|
||||
|
||||
// 从JWT中获取用户名进行匹配
|
||||
String tokenUsername = claimsJws.getBody().getSubject();
|
||||
|
||||
// 验证用户名是否匹配
|
||||
return username.equals(tokenUsername);
|
||||
return Pattern.matches("^[0-9A-Za-z_]{3,40}$", tokenUsername);
|
||||
} catch (Exception e) {
|
||||
// 验证失败
|
||||
return false;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.jsl.oa.utils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
|
||||
@ -25,7 +26,7 @@ public class Processing {
|
||||
* @param bindingResult 参数校验结果
|
||||
* @return {@link ArrayList<String>}
|
||||
*/
|
||||
public static ArrayList<String> getValidatedErrorList(BindingResult bindingResult) {
|
||||
public static @NotNull ArrayList<String> getValidatedErrorList(BindingResult bindingResult) {
|
||||
ArrayList<String> arrayList = new ArrayList<>();
|
||||
for (ObjectError objectError : bindingResult.getAllErrors()) {
|
||||
arrayList.add(objectError.getDefaultMessage());
|
||||
@ -42,7 +43,7 @@ public class Processing {
|
||||
* @param type 0:学生 1:教师 2:其他
|
||||
* @return {@link String}
|
||||
*/
|
||||
public static String createJobNumber(Short type) {
|
||||
public static @NotNull String createJobNumber(Short type) {
|
||||
return createJobNumber(type, (short) 10);
|
||||
}
|
||||
|
||||
@ -56,7 +57,7 @@ public class Processing {
|
||||
* @param size 工号长度
|
||||
* @return {@link String}
|
||||
*/
|
||||
public static String createJobNumber(Short type, Short size) {
|
||||
public static @NotNull String createJobNumber(Short type, Short size) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (type == 0) {
|
||||
stringBuilder.append("STU");
|
||||
@ -72,4 +73,17 @@ public class Processing {
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static @NotNull String createCode() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
// 生成验证码
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
stringBuilder.append(random.nextInt(10));
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
0
src/main/resources/.gitkeep
Normal file
0
src/main/resources/.gitkeep
Normal file
@ -8,6 +8,8 @@ spring:
|
||||
database: 0
|
||||
host: localhost
|
||||
port: 6379
|
||||
profiles:
|
||||
active: dev
|
||||
mybatis:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
@ -10,7 +10,7 @@ class JslOrganizeInternalOaApplicationTests {
|
||||
@Test
|
||||
void contextLoads() {
|
||||
String token = JwtUtil.generateToken("admin");
|
||||
if (JwtUtil.verify(token, "admin")) {
|
||||
if (JwtUtil.verify(token)) {
|
||||
System.out.println("验证通过");
|
||||
} else {
|
||||
System.out.println("验证失败");
|
||||
|
Loading…
x
Reference in New Issue
Block a user