完成子账户接口设计
This commit is contained in:
parent
48acb638e7
commit
fa630a1d89
@ -56,7 +56,8 @@ public class SecurityConfiguration {
|
||||
.requestMatchers("/api/auth/**", "/error").permitAll()
|
||||
.requestMatchers("/monitor/**").permitAll()
|
||||
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
|
||||
.anyRequest().hasAnyRole(Const.ROLE_DEFAULT)
|
||||
.requestMatchers("/api/user/sub/**").hasRole(Const.ROLE_ADMIN)
|
||||
.anyRequest().hasAnyRole(Const.ROLE_ADMIN, Const.ROLE_NORMAL)
|
||||
)
|
||||
.formLogin(conf -> conf
|
||||
.loginProcessingUrl("/api/auth/login")
|
||||
|
@ -2,12 +2,16 @@ package com.example.controller;
|
||||
|
||||
import com.example.entity.RestBean;
|
||||
import com.example.entity.vo.request.ChangePasswordVO;
|
||||
import com.example.entity.vo.request.CreateSubAccountVO;
|
||||
import com.example.entity.vo.response.SubAccountVO;
|
||||
import com.example.service.AccountService;
|
||||
import com.example.utils.Const;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
@ -22,4 +26,23 @@ public class UserController {
|
||||
RestBean.success() : RestBean.failure(401, "原密码输入错误!");
|
||||
}
|
||||
|
||||
@PostMapping("/sub/create")
|
||||
public RestBean<Void> createSubAccount(@RequestBody @Valid CreateSubAccountVO vo) {
|
||||
service.createSubAccount(vo);
|
||||
return RestBean.success();
|
||||
}
|
||||
|
||||
@GetMapping("/sub/delete")
|
||||
public RestBean<Void> deleteSubAccount(int uid,
|
||||
@RequestAttribute(Const.ATTR_USER_ID) int userId) {
|
||||
if(uid == userId)
|
||||
return RestBean.failure(401, "非法参数");
|
||||
service.deleteSubAccount(uid);
|
||||
return RestBean.success();
|
||||
}
|
||||
|
||||
@GetMapping("/sub/list")
|
||||
public RestBean<List<SubAccountVO>> subAccountList() {
|
||||
return RestBean.success(service.listSubAccount());
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,5 @@ public class Account implements BaseData {
|
||||
String email;
|
||||
String role;
|
||||
Date registerTime;
|
||||
String clients;
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.example.entity.vo.request;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CreateSubAccountVO {
|
||||
@Length(min = 1, max = 10)
|
||||
String username;
|
||||
@Email
|
||||
String email;
|
||||
@Length(min = 6, max = 20)
|
||||
String password;
|
||||
@Size(min = 1)
|
||||
List<Integer> clients;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.example.entity.vo.response;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SubAccountVO {
|
||||
int id;
|
||||
String username;
|
||||
String email;
|
||||
JSONArray clientList;
|
||||
}
|
@ -19,6 +19,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 用于对请求头中Jwt令牌进行校验的工具,为当前请求添加用户验证信息
|
||||
@ -60,6 +61,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
request.setAttribute(Const.ATTR_USER_ID, utils.toId(jwt));
|
||||
request.setAttribute(Const.ATTR_USER_ROLE, new ArrayList<>(user.getAuthorities()).get(0).getAuthority());
|
||||
}
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
|
@ -3,13 +3,20 @@ package com.example.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.example.entity.dto.Account;
|
||||
import com.example.entity.vo.request.ConfirmResetVO;
|
||||
import com.example.entity.vo.request.CreateSubAccountVO;
|
||||
import com.example.entity.vo.request.EmailResetVO;
|
||||
import com.example.entity.vo.response.SubAccountVO;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AccountService extends IService<Account>, UserDetailsService {
|
||||
Account findAccountByNameOrEmail(String text);
|
||||
String registerEmailVerifyCode(String type, String email, String address);
|
||||
String resetEmailAccountPassword(EmailResetVO info);
|
||||
String resetConfirm(ConfirmResetVO info);
|
||||
boolean changePassword(int id, String oldPass, String newPass);
|
||||
void createSubAccount(CreateSubAccountVO vo);
|
||||
void deleteSubAccount(int uid);
|
||||
List<SubAccountVO> listSubAccount();
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package com.example.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.example.entity.dto.Account;
|
||||
import com.example.entity.vo.request.ConfirmResetVO;
|
||||
import com.example.entity.vo.request.CreateSubAccountVO;
|
||||
import com.example.entity.vo.request.EmailResetVO;
|
||||
import com.example.entity.vo.response.SubAccountVO;
|
||||
import com.example.mapper.AccountMapper;
|
||||
import com.example.service.AccountService;
|
||||
import com.example.utils.Const;
|
||||
@ -19,6 +22,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -127,6 +132,35 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSubAccount(CreateSubAccountVO vo) {
|
||||
Account account = this.findAccountByNameOrEmail(vo.getEmail());
|
||||
if(account != null)
|
||||
throw new IllegalArgumentException("该电子邮件已被注册");
|
||||
account = this.findAccountByNameOrEmail(vo.getUsername());
|
||||
if(account != null)
|
||||
throw new IllegalArgumentException("该用户名已被注册");
|
||||
account = new Account(null, vo.getUsername(), passwordEncoder.encode(vo.getPassword()),
|
||||
vo.getEmail(), Const.ROLE_NORMAL, new Date(), JSONArray.copyOf(vo.getClients()).toJSONString());
|
||||
this.save(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSubAccount(int uid) {
|
||||
this.removeById(uid);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SubAccountVO> listSubAccount() {
|
||||
return this.list(Wrappers.<Account>query().eq("role", Const.ROLE_NORMAL))
|
||||
.stream().map(account -> {
|
||||
SubAccountVO vo = account.asViewObject(SubAccountVO.class);
|
||||
vo.setClientList(JSONArray.parse(account.getClients()));
|
||||
return vo;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Redis中存储的邮件验证码
|
||||
* @param email 电邮
|
||||
|
@ -7,6 +7,8 @@ public final class Const {
|
||||
//JWT令牌
|
||||
public final static String JWT_BLACK_LIST = "jwt:blacklist:";
|
||||
public final static String JWT_FREQUENCY = "jwt:frequency:";
|
||||
//用户
|
||||
public final static String USER_BLACK_LIST = "user:blacklist:";
|
||||
//请求频率限制
|
||||
public final static String FLOW_LIMIT_COUNTER = "flow:counter:";
|
||||
public final static String FLOW_LIMIT_BLOCK = "flow:block:";
|
||||
@ -18,10 +20,11 @@ public final class Const {
|
||||
public final static int ORDER_CORS = -102;
|
||||
//请求自定义属性
|
||||
public final static String ATTR_USER_ID = "userId";
|
||||
public final static String ATTR_USER_ROLE = "userRole";
|
||||
public final static String ATTR_CLIENT = "client";
|
||||
//消息队列
|
||||
public final static String MQ_MAIL = "mail";
|
||||
//用户角色
|
||||
public final static String ROLE_DEFAULT = "admin";
|
||||
|
||||
public final static String ROLE_ADMIN = "admin";
|
||||
public final static String ROLE_NORMAL = "user";
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ public class JwtUtils {
|
||||
try {
|
||||
DecodedJWT verify = jwtVerifier.verify(token);
|
||||
if(this.isInvalidToken(verify.getId())) return null;
|
||||
if(this.isInvalidUser(verify.getClaim("id").asInt())) return null;
|
||||
Map<String, Claim> claims = verify.getClaims();
|
||||
return new Date().after(claims.get("exp").asDate()) ? null : verify;
|
||||
} catch (JWTVerificationException e) {
|
||||
@ -177,6 +178,14 @@ public class JwtUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void deleteUser(int uid) {
|
||||
template.opsForValue().set(Const.USER_BLACK_LIST + uid, "", expire, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
private boolean isInvalidUser(int uid){
|
||||
return Boolean.TRUE.equals(template.hasKey(Const.USER_BLACK_LIST + uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证Token是否被列入Redis黑名单
|
||||
* @param uuid 令牌ID
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import {reactive, ref} from "vue";
|
||||
import {Lock, Switch} from "@element-plus/icons-vue";
|
||||
import {Lock, Plus, Switch} from "@element-plus/icons-vue";
|
||||
import {logout, post} from "@/net";
|
||||
import {ElMessage} from "element-plus";
|
||||
import router from "@/router";
|
||||
@ -56,6 +56,8 @@ function resetPassword() {
|
||||
<div style="display: flex;gap: 10px">
|
||||
<div style="flex: 50%">
|
||||
<div class="info-card">
|
||||
<div class="title"><i class="fa-solid fa-lock"></i> 修改密码</div>
|
||||
<el-divider style="margin: 10px 0"/>
|
||||
<el-form @validate="onValidate" :model="form" :rules="rules"
|
||||
ref="formRef" style="margin: 20px" label-width="100">
|
||||
<el-form-item label="当前密码" prop="password">
|
||||
@ -81,7 +83,11 @@ function resetPassword() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-card" style="flex: 50%">
|
||||
|
||||
<div class="title"><i class="fa-solid fa-users"></i> 子用户管理</div>
|
||||
<el-divider style="margin: 10px 0"/>
|
||||
<el-empty :image-size="100" description="还没有任何子用户哦">
|
||||
<el-button :icon="Plus" type="primary" plain>添加子用户</el-button>
|
||||
</el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -91,5 +97,12 @@ function resetPassword() {
|
||||
border-radius: 7px;
|
||||
padding: 15px 20px;
|
||||
background-color: var(--el-bg-color);
|
||||
height: fit-content;
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: dodgerblue;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user