完成隐私设置

This commit is contained in:
柏码の讲师 2023-06-19 17:14:50 +08:00
parent fdbd412881
commit 600efd92c3
9 changed files with 169 additions and 24 deletions

View File

@ -9,6 +9,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -112,6 +113,10 @@ public class SecurityConfiguration {
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
response.setCharacterEncoding("utf-8");
response.getWriter().write(JSONObject.toJSONString(RestBean.failure(401, exception.getMessage())));
if(exception instanceof BadCredentialsException) {
response.getWriter().write(JSONObject.toJSONString(RestBean.failure(403, exception.getMessage())));
} else {
response.getWriter().write(JSONObject.toJSONString(RestBean.failure(401, exception.getMessage())));
}
}
}

View File

@ -5,13 +5,18 @@ import com.example.entity.user.AccountInfo;
import com.example.entity.user.AccountUser;
import com.example.service.UserService;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Validated
@RestController
@RequestMapping("/api/user")
public class UserController {
private final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$";
@Resource
UserService service;
@ -36,4 +41,31 @@ public class UserController {
public RestBean<AccountInfo> info(@SessionAttribute("account") AccountUser user){
return RestBean.success(service.userInfo(user.getId()));
}
@PostMapping("/save-email")
public RestBean<String> saveInfo(@Pattern(regexp = EMAIL_REGEX) @RequestParam("email") String email,
@SessionAttribute("account") AccountUser user){
if(service.saveEmail(email, user.getId())) {
user.setEmail(email);
return RestBean.success();
} else {
return RestBean.failure(400, "邮件地址已被其他用户使用,无法修改");
}
}
@GetMapping("/email")
public RestBean<String> email(@SessionAttribute("account") AccountUser user){
return RestBean.success(user.getEmail());
}
@PostMapping("/save-password")
public RestBean<String> savePassword(@Length(min = 6, max = 16) @RequestParam("old") String pass_old,
@Length(min = 6, max = 16) @RequestParam("new") String pass_new,
@SessionAttribute("account") AccountUser user){
if (service.changePassword(pass_old, pass_new, user.getId())) {
return RestBean.success();
} else {
return RestBean.failure(400, "原密码错误");
}
}
}

View File

@ -1,11 +1,15 @@
package com.example.entity.user;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
@Data
public class AccountInfo {
private static final String USERNAME_REGEX = "^[a-zA-Z0-9一-龥]+$";
int uid;
@Pattern(regexp = USERNAME_REGEX)
@Length(min = 2, max = 8)
String username;
String sex;

View File

@ -13,6 +13,9 @@ public interface UserMapper {
@Select("select * from db_account where username = #{text} or email = #{text}")
Account findAccountByNameOrEmail(String text);
@Select("select * from db_account where id = #{id}")
Account findAccountById(int id);
@Select("select * from db_account where username = #{text} or email = #{text}")
AccountUser findAccountUserByNameOrEmail(String text);
@ -35,4 +38,10 @@ public interface UserMapper {
@Select("select * from db_account_info left join db_account on id = uid where id = #{uid}")
AccountInfo findInfoById(int uid);
@Update("update db_account set email=#{email} where id=#{uid}")
void updateEmail(String email, int uid);
@Update("update db_account set password=#{password} where id=#{uid}")
void updatePassword(String password, int uid);
}

View File

@ -5,4 +5,6 @@ import com.example.entity.user.AccountInfo;
public interface UserService {
boolean saveUserInfo(AccountInfo info);
AccountInfo userInfo(int uid);
boolean saveEmail(String email, int uid);
boolean changePassword(String old, String _new, int uid);
}

View File

@ -5,6 +5,7 @@ import com.example.entity.user.AccountInfo;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@ -12,6 +13,8 @@ public class UserServiceImpl implements UserService {
@Resource
UserMapper mapper;
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
@Override
public boolean saveUserInfo(AccountInfo info) {
Account account = mapper.findAccountByNameOrEmail(info.getUsername());
@ -28,4 +31,25 @@ public class UserServiceImpl implements UserService {
public AccountInfo userInfo(int uid) {
return mapper.findInfoById(uid);
}
@Override
public boolean saveEmail(String email, int uid) {
Account account = mapper.findAccountByNameOrEmail(email);
if(account == null) {
mapper.updateEmail(email, uid);
} else return account.getId() == uid;
return true;
}
@Override
public boolean changePassword(String old, String _new, int uid) {
Account account = mapper.findAccountById(uid);
if (encoder.matches(old, account.getPassword())) {
String encode = encoder.encode(_new);
mapper.updatePassword(encode, uid);
return true;
} else {
return false;
}
}
}

View File

@ -1,13 +1,79 @@
<script setup>
import {reactive} from "vue";
import {onMounted, reactive, ref} from "vue";
import {Lock, Message, Select} from "@element-plus/icons-vue";
import {get, logout, post} from "@/net";
import {ElMessage} from "element-plus";
const securityForm = reactive({
email: '',
email: null,
password_old: '',
password_new: '',
password_new_repeat: '',
})
const emailForm = ref()
const saveEmail = () => {
emailForm.value.validate((isValid) => {
if(isValid) {
post('/api/user/save-email', {email: securityForm.email},
() => ElMessage.success("保存成功!"))
} else {
ElMessage.warning('邮件格式有误,请正确填写')
}
})
}
const passwordForm = ref()
const changePassword = () => {
passwordForm.value.validate((isValid) => {
if(isValid) {
post('/api/user/save-password', {
old: securityForm.password_old,
new: securityForm.password_new
}, () => {
ElMessage.success('密码修改成功,请重新登录!')
logout()
})
} else {
ElMessage.warning('密码校验失败,请检查是否正确填写')
}
})
}
onMounted(() => {
if(securityForm.email == null) {
get('/api/user/email', message => securityForm.email = message)
}
})
const validatePassword = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== securityForm.password_new) {
callback(new Error("两次输入的密码不一致"))
} else {
callback()
}
}
const rules = {
password_old: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 16, message: '密码的长度必须在6-16个字符之间', trigger: ['blur', 'change'] }
],
password_new: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 16, message: '密码的长度必须在6-16个字符之间', trigger: ['blur', 'change'] }
],
password_new_repeat: [
{ required: true, message: '请再次输入密码', trigger: 'blur' },
{ validator: validatePassword, trigger: ['blur', 'change'] },
],
email: [
{ required: true, message: '请输入邮件地址', trigger: 'blur' },
{ type: 'email', message: '请输入合法的电子邮件地址', trigger: ['blur', 'change']}
]
}
</script>
<template>
@ -15,35 +81,39 @@ const securityForm = reactive({
<div>
<h1><el-icon><Message/></el-icon> 邮箱设置</h1>
<el-form
ref="emailForm"
:rules="rules"
label-position="top"
label-width="100px"
:model="securityForm"
style="max-width: 460px">
<el-form-item label="邮箱地址">
<el-form-item label="邮箱地址" prop="email">
<el-input v-model="securityForm.email" />
</el-form-item>
</el-form>
<el-button type="success" :icon="Select">保存邮件地址</el-button>
<el-button type="success" :icon="Select" @click="saveEmail">保存邮件地址</el-button>
</div>
<el-divider/>
<div>
<h1><el-icon><Lock/></el-icon> 密码设置</h1>
<el-form
ref="passwordForm"
:rules="rules"
label-position="top"
label-width="100px"
:model="securityForm"
style="max-width: 460px">
<el-form-item label="原密码">
<el-input v-model="securityForm.password_old" />
<el-form-item prop="password_old" label="原密码">
<el-input type="password" show-password v-model="securityForm.password_old" />
</el-form-item>
<el-form-item label="新密码">
<el-input v-model="securityForm.password_old" />
<el-form-item prop="password_new" label="新密码">
<el-input type="password" show-password v-model="securityForm.password_new" />
</el-form-item>
<el-form-item label="重复新密码">
<el-input v-model="securityForm.password_old" />
<el-form-item prop="password_new_repeat" label="重复新密码">
<el-input type="password" show-password v-model="securityForm.password_new_repeat" />
</el-form-item>
</el-form>
<el-button type="success" :icon="Select">修改密码</el-button>
<el-button type="success" :icon="Select" @click="changePassword">修改密码</el-button>
</div>
</div>
</template>

View File

@ -41,4 +41,13 @@ function get(url, success, failure = defaultFailure, error = defaultError) {
}).catch(error)
}
export { get, post }
const logout = () => {
get('/api/auth/logout', (message) => {
ElMessage.success(message)
store.auth.user = null
localStorage.removeItem('user')
router.push('/')
})
}
export { get, post, logout }

View File

@ -84,8 +84,7 @@
</template>
<script setup>
import {get} from "@/net";
import {ElMessage} from "element-plus";
import {logout} from "@/net";
import router from "@/router";
import {useStore} from "@/stores";
import {Document, Setting, Menu as IconMenu, Search, Back, Expand, Fold} from "@element-plus/icons-vue";
@ -94,15 +93,6 @@ import {ref} from "vue";
const store = useStore()
const isCollapse = ref(false)
const logout = () => {
get('/api/auth/logout', (message) => {
ElMessage.success(message)
store.auth.user = null
localStorage.removeItem('user')
router.push('/')
})
}
</script>
<style scoped>