feat(login): 添加记住我登录功能
前端新增记住我勾选框,登录页挂载时自动恢复已保存的用户名,勾选时将用户名存入本地存储并在登录请求中携带对应参数;后端新增rememberMe参数处理逻辑,配置7天免登录的JWT有效期,重载JWT工具类以支持自定义令牌过期时间
This commit is contained in:
parent
f400a93bdd
commit
058e1fb8a7
|
|
@ -24,4 +24,9 @@ public class LoginDTO implements Serializable {
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "密码不能为空")
|
@NotBlank(message = "密码不能为空")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记住我(true 时签发 7 天有效期的 token)
|
||||||
|
*/
|
||||||
|
private Boolean rememberMe = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,10 @@ public class UserServiceImpl implements UserService {
|
||||||
throw new BusinessException("用户名或密码错误");
|
throw new BusinessException("用户名或密码错误");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(loginDTO.getRememberMe())) {
|
||||||
|
return jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole(),
|
||||||
|
user.getDealerId(), jwtUtil.getRememberExpiration());
|
||||||
|
}
|
||||||
return jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole(), user.getDealerId());
|
return jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole(), user.getDealerId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,24 +25,45 @@ public class JwtUtil {
|
||||||
@Value("${jwt.expiration}")
|
@Value("${jwt.expiration}")
|
||||||
private Long expiration;
|
private Long expiration;
|
||||||
|
|
||||||
|
@Value("${jwt.remember-expiration:604800000}")
|
||||||
|
private Long rememberExpiration;
|
||||||
|
|
||||||
|
public Long getRememberExpiration() {
|
||||||
|
return rememberExpiration;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 Token
|
* 生成 Token
|
||||||
*/
|
*/
|
||||||
public String generateToken(Long userId, String username, Integer role, Long dealerId) {
|
public String generateToken(Long userId, String username, Integer role, Long dealerId) {
|
||||||
|
return generateToken(userId, username, role, dealerId, expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成指定有效期的 Token(用于"记住我"延长有效期)
|
||||||
|
*/
|
||||||
|
public String generateToken(Long userId, String username, Integer role, Long dealerId, long expirationMillis) {
|
||||||
Map<String, Object> claims = new HashMap<>();
|
Map<String, Object> claims = new HashMap<>();
|
||||||
claims.put("userId", userId);
|
claims.put("userId", userId);
|
||||||
claims.put("username", username);
|
claims.put("username", username);
|
||||||
claims.put("role", role);
|
claims.put("role", role);
|
||||||
claims.put("dealerId", dealerId);
|
claims.put("dealerId", dealerId);
|
||||||
return generateToken(claims);
|
return generateToken(claims, expirationMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 Token
|
* 生成 Token
|
||||||
*/
|
*/
|
||||||
public String generateToken(Map<String, Object> claims) {
|
public String generateToken(Map<String, Object> claims) {
|
||||||
|
return generateToken(claims, expiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成指定有效期的 Token
|
||||||
|
*/
|
||||||
|
public String generateToken(Map<String, Object> claims, long expirationMillis) {
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
Date expiryDate = new Date(now.getTime() + expiration);
|
Date expiryDate = new Date(now.getTime() + expirationMillis);
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setClaims(claims)
|
.setClaims(claims)
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ jwt:
|
||||||
# 生产环境请使用环境变量或密钥管理系统存储此密钥
|
# 生产环境请使用环境变量或密钥管理系统存储此密钥
|
||||||
secret: by-crm-jwt-secret-key-2024-hs512-requires-at-least-64-bytes-for-secure-signing-please-change-in-production-environment
|
secret: by-crm-jwt-secret-key-2024-hs512-requires-at-least-64-bytes-for-secure-signing-please-change-in-production-environment
|
||||||
expiration: 86400000 # 24小时,单位:毫秒
|
expiration: 86400000 # 24小时,单位:毫秒
|
||||||
|
remember-expiration: 604800000 # 记住我:7天免登录,单位:毫秒
|
||||||
|
|
||||||
# CRM 业务配置
|
# CRM 业务配置
|
||||||
crm:
|
crm:
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export interface User {
|
||||||
export interface LoginRequest {
|
export interface LoginRequest {
|
||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
|
rememberMe?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@
|
||||||
@keyup.enter="handleLogin"
|
@keyup.enter="handleLogin"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="remember">记住我(7天免登录)</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" size="large" :loading="loading" @click="handleLogin" class="login-btn">
|
<el-button type="primary" size="large" :loading="loading" @click="handleLogin" class="login-btn">
|
||||||
登录
|
登录
|
||||||
|
|
@ -32,7 +35,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
@ -49,6 +52,16 @@ const loginForm = reactive({
|
||||||
password: ''
|
password: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const remember = ref(false)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const savedUsername = localStorage.getItem('remember-username')
|
||||||
|
if (savedUsername) {
|
||||||
|
loginForm.username = savedUsername
|
||||||
|
remember.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const rules: FormRules = {
|
const rules: FormRules = {
|
||||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
||||||
|
|
@ -64,8 +77,15 @@ const handleLogin = async () => {
|
||||||
const hashedPassword = await hashPassword(loginForm.password)
|
const hashedPassword = await hashPassword(loginForm.password)
|
||||||
await userStore.login({
|
await userStore.login({
|
||||||
username: loginForm.username,
|
username: loginForm.username,
|
||||||
password: hashedPassword
|
password: hashedPassword,
|
||||||
|
rememberMe: remember.value
|
||||||
})
|
})
|
||||||
|
// 记住用户名(勾选"记住我"时保存,否则清除)
|
||||||
|
if (remember.value) {
|
||||||
|
localStorage.setItem('remember-username', loginForm.username)
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('remember-username')
|
||||||
|
}
|
||||||
ElMessage.success('登录成功')
|
ElMessage.success('登录成功')
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user