diff --git a/pom.xml b/pom.xml
index 9791ec4..0292c13 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,9 +64,28 @@
org.apache.shiro
- shiro-spring
+ shiro-spring-boot-starter
1.9.1
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.3
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.3
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.3
+ runtime
+
+
org.mybatis.spring.boot
diff --git a/src/main/java/com/jsl/oa/config/JwtToken.java b/src/main/java/com/jsl/oa/config/JwtToken.java
new file mode 100644
index 0000000..c3dde62
--- /dev/null
+++ b/src/main/java/com/jsl/oa/config/JwtToken.java
@@ -0,0 +1,23 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/jsl/oa/config/MyRealm.java b/src/main/java/com/jsl/oa/config/MyRealm.java
index 76d01ce..740eed8 100644
--- a/src/main/java/com/jsl/oa/config/MyRealm.java
+++ b/src/main/java/com/jsl/oa/config/MyRealm.java
@@ -1,6 +1,7 @@
package com.jsl.oa.config;
-import com.jsl.oa.services.AccountService;
+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;
@@ -10,7 +11,7 @@ import org.apache.shiro.subject.PrincipalCollection;
@RequiredArgsConstructor
public class MyRealm extends AuthorizingRealm {
- private final AccountService accountService;
+ private final UserService userService;
/**
* 授权
@@ -30,11 +31,21 @@ public class MyRealm extends AuthorizingRealm {
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
- Account account = accountService.findByUsername(token.getUsername());
- if(account != null){
- return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
+ 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 null;
+
+ return new SimpleAuthenticationInfo(username, jwtToken.getCredentials(), getName());
}
}
diff --git a/src/main/java/com/jsl/oa/config/ShiroConfiguration.java b/src/main/java/com/jsl/oa/config/ShiroConfiguration.java
index 046672d..ec861a6 100644
--- a/src/main/java/com/jsl/oa/config/ShiroConfiguration.java
+++ b/src/main/java/com/jsl/oa/config/ShiroConfiguration.java
@@ -1,44 +1,46 @@
package com.jsl.oa.config;
+import com.jsl.oa.services.UserService;
+import lombok.RequiredArgsConstructor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
-import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
+@RequiredArgsConstructor
public class ShiroConfiguration {
- @Bean
- public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
- // ShiroFilterFactoryBean 用来配置拦截规则
- ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
- factoryBean.setSecurityManager(manager);
- // 设置拦截规则
- Map map = new HashMap<>();
- map.put("/main","authc");
- map.put("/manage","perms[manage]");
- map.put("/administrator","roles[administrator]");
- factoryBean.setFilterChainDefinitionMap(map);
- //未授权页面
- factoryBean.setUnauthorizedUrl("/unauth");
- return factoryBean;
- }
-
-
+ private final UserService userService;
@Bean
- public DefaultWebSecurityManager manager(@Qualifier("myRealm") MyRealm myRealm){
- DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
- manager.setRealm(myRealm);
- return manager;
+ public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
+ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+ shiroFilterFactoryBean.setSecurityManager(securityManager);
+
+ // 配置过滤器规则
+ Map filterChainDefinitionMap = new LinkedHashMap<>();
+ filterChainDefinitionMap.put("/auth/**", "anon"); // 登录接口允许匿名访问
+
+ shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+
+ // 设置登录接口
+ shiroFilterFactoryBean.setLoginUrl("/unauthorized");
+ return shiroFilterFactoryBean;
}
@Bean
- public MyRealm myRealm(){
- return new MyRealm();
+ public DefaultWebSecurityManager securityManager(MyRealm realm) {
+ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+ securityManager.setRealm(realm);
+ return securityManager;
+ }
+
+ @Bean
+ public MyRealm myRealm() {
+ return new MyRealm(userService);
}
}
diff --git a/src/main/java/com/jsl/oa/controllers/AuthController.java b/src/main/java/com/jsl/oa/controllers/AuthController.java
index 7497693..c7bb20a 100644
--- a/src/main/java/com/jsl/oa/controllers/AuthController.java
+++ b/src/main/java/com/jsl/oa/controllers/AuthController.java
@@ -10,26 +10,40 @@ 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 org.springframework.web.bind.annotation.*;
import java.text.ParseException;
+/**
+ * 用户认证控制器
+ *
+ * 用户认证控制器,包含用户注册、用户登录、用户登出接口
+ *
+ * @since v1.0.0
+ * @version v1.1.0
+ * @see AuthService
+ * @see UserRegisterVO
+ * @see UserLoginVO
+ * @see BaseResponse
+ * @see ErrorCode
+ * @see Processing
+ * @see ResultUtil
+ */
@RestController
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
/**
- * 用户注册
+ * 用户注册
*
* 用户注册接口
*
+ * @since v1.0.0
* @return {@link BaseResponse}
* @author 筱锋xiao_lfeng
*/
- @PostMapping("/user/register")
+ @PostMapping("/auth/register")
public BaseResponse authRegister(@RequestBody @Validated UserRegisterVO userRegisterVO, BindingResult bindingResult) throws ParseException {
// 判断是否有参数错误
if (bindingResult.hasErrors()) {
@@ -39,7 +53,7 @@ public class AuthController {
}
/**
- * 用户登录
+ * 用户登录
*
* 用户登录接口
*
@@ -48,7 +62,7 @@ public class AuthController {
* @return {@link BaseResponse}
* @author 176yunxuan
*/
- @PostMapping("/user/login")
+ @GetMapping("/auth/login")
public BaseResponse authLogin(@RequestBody @Validated UserLoginVO userLoginVO, BindingResult bindingResult){
// 判断是否有参数错误
if (bindingResult.hasErrors()) {
@@ -56,4 +70,8 @@ public class AuthController {
}
return authService.authLogin(userLoginVO);
}
+
+ public BaseResponse authLogout() {
+ return null;
+ }
}
diff --git a/src/main/java/com/jsl/oa/controllers/CustomController.java b/src/main/java/com/jsl/oa/controllers/CustomController.java
index 668bd3f..7f62c84 100644
--- a/src/main/java/com/jsl/oa/controllers/CustomController.java
+++ b/src/main/java/com/jsl/oa/controllers/CustomController.java
@@ -19,4 +19,9 @@ public class CustomController implements ErrorController {
public ResponseEntity handleError() {
return ResultUtil.error("PageNotFound", 404, "请求资源不存在");
}
+
+ @RequestMapping("/unauthorized")
+ public ResponseEntity handleUnauthorized() {
+ return ResultUtil.error("Unauthorized", 401, "未授权");
+ }
}
diff --git a/src/main/java/com/jsl/oa/dao/UserDAO.java b/src/main/java/com/jsl/oa/dao/UserDAO.java
new file mode 100644
index 0000000..9ac1bd7
--- /dev/null
+++ b/src/main/java/com/jsl/oa/dao/UserDAO.java
@@ -0,0 +1,24 @@
+package com.jsl.oa.dao;
+
+import com.jsl.oa.mapper.UserMapper;
+import com.jsl.oa.model.doData.UserDO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class UserDAO {
+
+ private final UserMapper userMapper;
+
+ public UserDO getUserInfoByUsername(String username) {
+ UserDO userDO = null;
+ // 从 Redis 获取数据
+ // TODO: 10000-Redis: 从 Redis 获取数据
+ // 从数据库获取用户信息
+ if (userDO == null) {
+ userDO = userMapper.getUserInfoByUsername(username);
+ }
+ return userDO;
+ }
+}
diff --git a/src/main/java/com/jsl/oa/mapper/UserMapper.java b/src/main/java/com/jsl/oa/mapper/UserMapper.java
index d0d9e6f..a7d1bd6 100644
--- a/src/main/java/com/jsl/oa/mapper/UserMapper.java
+++ b/src/main/java/com/jsl/oa/mapper/UserMapper.java
@@ -10,19 +10,19 @@ import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("SELECT * FROM organize_oa.oa_user WHERE username = #{username}")
- UserDO getUserByUsername(String username);
+ UserDO getUserInfoByUsername(String username);
- @Select("SELECT * FROM organize_oa.oa_user WHERE user_num = #{userNum}")
- UserDO getUserByUserNum(String userNum);
+ @Select("SELECT * FROM organize_oa.oa_user WHERE job_id = #{jobId}")
+ UserDO getUserByUserNum(String jobId);
- @Insert("INSERT INTO organize_oa.oa_user (user_num, username, password, sex, age, unit, field, hometown, kind, state) " +
- "VALUES " +
- "(#{userNum}, #{username}, #{password}, #{sex}, #{age}, #{unit}, #{filed}, #{hometown}, #{kind}, #{state})")
- Boolean insertUser(UserDO userDO);
+ @Insert("INSERT INTO organize_oa.oa_user " +
+ "(job_id, username, password, address, phone, email, age, signature, avatar, nickname, account_no_locked, description, updated_at) " +
+ "VALUES (#{jobId}, #{username}, #{password}, #{address}, #{phone}, #{email}, #{age}, #{signature}, #{avatar}, #{nickname}, #{accountNoLocked}, #{description}, #{updatedAt})")
+ boolean insertUser(UserDO userDO);
- @Select("select id, user_num, username, sex, age, unit, field, hometown, kind, state from organize_oa.oa_user where user_num = #{userNum} ")
- UserDO login(UserLoginVO userLoginVO);
-
- @Select("select password from organize_oa.oa_user where user_num = #{userNum}")
+ @Select("SELECT password FROM organize_oa.oa_user WHERE job_id = #{jobId}")
String loginPassword(UserLoginVO userLoginVO);
+
+ @Select("SELECT * FROM organize_oa.oa_user WHERE job_id = #{jobId}")
+ UserDO login(UserLoginVO userLoginVO);
}
diff --git a/src/main/java/com/jsl/oa/model/doData/UserDO.java b/src/main/java/com/jsl/oa/model/doData/UserDO.java
index b4e8179..184c5a2 100644
--- a/src/main/java/com/jsl/oa/model/doData/UserDO.java
+++ b/src/main/java/com/jsl/oa/model/doData/UserDO.java
@@ -20,7 +20,7 @@ import java.sql.Timestamp;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserDO {
private Long id;
- private Long jobId;
+ private String jobId;
private String username;
private String password;
private String address;
diff --git a/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java b/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java
index 47356a6..25c4ce6 100644
--- a/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java
+++ b/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java
@@ -17,7 +17,7 @@ import javax.validation.constraints.Pattern;
@Getter
public class UserLoginVO {
@Pattern(regexp = "^[0-9A-Z]+$", message = "工号格式错误")
- private String userNum;
+ private String jobId;
@NotBlank(message = "密码不能为空")
private String password;
}
diff --git a/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java b/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java
index c8b8c7a..6745d96 100644
--- a/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java
+++ b/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java
@@ -23,11 +23,11 @@ public class UserRegisterVO {
@NotBlank(message = "密码不能为空")
private String password;
- @Pattern(regexp = "^(男|女|保密)$", message = "性别只能为男、女或保密")
- private String sex;
+ @Pattern(regexp = "^[012]$", message = "保密:0,男:1,女:2")
+ private Short sex;
@NotBlank(message = "年龄不能为空")
- private String age;
+ private Short age;
@NotBlank(message = "单位不能为空")
private String unit;
diff --git a/src/main/java/com/jsl/oa/services/AccountService.java b/src/main/java/com/jsl/oa/services/AccountService.java
deleted file mode 100644
index 5baf437..0000000
--- a/src/main/java/com/jsl/oa/services/AccountService.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.jsl.oa.services;
-
-import org.apache.shiro.authc.Account;
-
-public interface AccountService {
- Account findByUsername(String username);
-}
diff --git a/src/main/java/com/jsl/oa/services/UserService.java b/src/main/java/com/jsl/oa/services/UserService.java
new file mode 100644
index 0000000..d0ebeb9
--- /dev/null
+++ b/src/main/java/com/jsl/oa/services/UserService.java
@@ -0,0 +1,25 @@
+package com.jsl.oa.services;
+
+import com.jsl.oa.model.doData.UserDO;
+
+/**
+ * 用户控制器接口
+ *
+ *
+ * 该接口用于定义用户控制器的方法
+ *
+ * @version 1.1.0
+ * @since v1.1.0
+ * @author 筱锋xiao_lfeng
+ */
+public interface UserService {
+ /**
+ * 根据用户名获取用户信息
+ *
+ * 该方法用于根据用户名获取用户信息
+ *
+ * @param username 用户名
+ * @return 用户信息
+ */
+ UserDO getUserInfoByUsername(String username);
+}
diff --git a/src/main/java/com/jsl/oa/services/impl/AccountServiceImpl.java b/src/main/java/com/jsl/oa/services/impl/AccountServiceImpl.java
deleted file mode 100644
index 740105f..0000000
--- a/src/main/java/com/jsl/oa/services/impl/AccountServiceImpl.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.jsl.oa.services.impl;
-
-import com.jsl.oa.services.AccountService;
-import lombok.RequiredArgsConstructor;
-import org.apache.shiro.authc.Account;
-
-@RequiredArgsConstructor
-public class AccountServiceImpl implements AccountService {
-
- private final AccountMapper accountMapper;
-
- @Override
- public Account findByUsername(String username) {
- QueryWrapper wrapper = new QueryWrapper();
- wrapper.eq("username",username);
- return accountMapper.selectOne(wrapper);
- }
-}
diff --git a/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java b/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java
index 2272001..a282f0b 100644
--- a/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java
+++ b/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java
@@ -14,9 +14,7 @@ import lombok.RequiredArgsConstructor;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.stereotype.Service;
-import java.sql.Date;
import java.text.ParseException;
-import java.text.SimpleDateFormat;
@Service
@RequiredArgsConstructor
@@ -33,9 +31,9 @@ public class AuthServiceImpl implements AuthService {
* @throws ParseException 日期转换异常
*/
@Override
- public BaseResponse authRegister(UserRegisterVO userRegisterVO) throws ParseException {
+ public BaseResponse authRegister(UserRegisterVO userRegisterVO) {
// 用户检查是否存在
- UserDO getUserByUsername = userMapper.getUserByUsername(userRegisterVO.getUsername());
+ UserDO getUserByUsername = userMapper.getUserInfoByUsername(userRegisterVO.getUsername());
// 用户名已存在
if (getUserByUsername != null) {
return ResultUtil.error(ErrorCode.USER_EXIST);
@@ -46,20 +44,15 @@ public class AuthServiceImpl implements AuthService {
do {
userNum = Processing.createJobNumber((short) 2);
} while (userMapper.getUserByUserNum(userNum) != null);
+ // 处理性别
// 数据上传
- Date getDate = new Date(new SimpleDateFormat("yyyy-MM-dd").parse(userRegisterVO.getAge()).getTime());
UserDO userDO = new UserDO();
- userDO.πsetUserNum(userNum)
+ userDO.setJobId(userNum)
.setUsername(userRegisterVO.getUsername())
.setPassword(BCrypt.hashpw(userRegisterVO.getPassword(), BCrypt.gensalt()))
.setSex(userRegisterVO.getSex())
- .setAge(getDate)
- .setUnit(userRegisterVO.getUnit())
- .setFiled(userRegisterVO.getFiled())
- .setHometown(userRegisterVO.getHometown())
- .setKind("0")
- .setState("0");
+ .setAge(userRegisterVO.getAge());
// 插入数据
if (userMapper.insertUser(userDO)) {
userDO.setPassword(null);
diff --git a/src/main/java/com/jsl/oa/services/impl/UserServiceImpl.java b/src/main/java/com/jsl/oa/services/impl/UserServiceImpl.java
new file mode 100644
index 0000000..e5c9a98
--- /dev/null
+++ b/src/main/java/com/jsl/oa/services/impl/UserServiceImpl.java
@@ -0,0 +1,19 @@
+package com.jsl.oa.services.impl;
+
+import com.jsl.oa.dao.UserDAO;
+import com.jsl.oa.model.doData.UserDO;
+import com.jsl.oa.services.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class UserServiceImpl implements UserService {
+
+ private final UserDAO userDAO;
+
+ @Override
+ public UserDO getUserInfoByUsername(String username) {
+ return userDAO.getUserInfoByUsername(username);
+ }
+}
diff --git a/src/main/java/com/jsl/oa/utils/JwtUtil.java b/src/main/java/com/jsl/oa/utils/JwtUtil.java
new file mode 100644
index 0000000..adf9801
--- /dev/null
+++ b/src/main/java/com/jsl/oa/utils/JwtUtil.java
@@ -0,0 +1,49 @@
+package com.jsl.oa.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+
+import java.security.Key;
+
+public class JwtUtil {
+ // 替换为实际的密钥,建议使用足够长的随机字符串
+ private static final String SECRET_KEY = Processing.createJobNumber((short) 1, (short) 255);
+
+ // Token 有效期,这里设置为一天,可以根据实际需求调整
+ private static final long EXPIRATION_TIME = 86400000;
+
+ // 生成Token
+ public static String generateToken(String username) {
+ Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
+
+ return Jwts.builder()
+ .setSubject(username)
+ .setExpiration(new java.util.Date(System.currentTimeMillis() + EXPIRATION_TIME))
+ .signWith(key, SignatureAlgorithm.HS256)
+ .compact();
+ }
+
+ // 验证Token
+ public static boolean verify(String token, String username) {
+ try {
+ Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
+
+ Jws claimsJws = Jwts.parserBuilder()
+ .setSigningKey(key)
+ .build()
+ .parseClaimsJws(token);
+
+ // 从JWT中获取用户名进行匹配
+ String tokenUsername = claimsJws.getBody().getSubject();
+
+ // 验证用户名是否匹配
+ return username.equals(tokenUsername);
+ } catch (Exception e) {
+ // 验证失败
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java b/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java
index 8c08a2e..96601ad 100644
--- a/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java
+++ b/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java
@@ -1,5 +1,6 @@
package com.jsl.oa;
+import com.jsl.oa.utils.JwtUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@@ -8,6 +9,12 @@ class JslOrganizeInternalOaApplicationTests {
@Test
void contextLoads() {
+ String token = JwtUtil.generateToken("admin");
+ if (JwtUtil.verify(token, "admin")) {
+ System.out.println("验证通过");
+ } else {
+ System.out.println("验证失败");
+ }
}
}