完成隐私设置

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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; 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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 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 { public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
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()))); 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.entity.user.AccountUser;
import com.example.service.UserService; import com.example.service.UserService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@Validated
@RestController @RestController
@RequestMapping("/api/user") @RequestMapping("/api/user")
public class UserController { public class UserController {
private final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$";
@Resource @Resource
UserService service; UserService service;
@ -36,4 +41,31 @@ public class UserController {
public RestBean<AccountInfo> info(@SessionAttribute("account") AccountUser user){ public RestBean<AccountInfo> info(@SessionAttribute("account") AccountUser user){
return RestBean.success(service.userInfo(user.getId())); 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; package com.example.entity.user;
import jakarta.validation.constraints.Pattern;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
@Data @Data
public class AccountInfo { public class AccountInfo {
private static final String USERNAME_REGEX = "^[a-zA-Z0-9一-龥]+$";
int uid; int uid;
@Pattern(regexp = USERNAME_REGEX)
@Length(min = 2, max = 8) @Length(min = 2, max = 8)
String username; String username;
String sex; String sex;

View File

@ -13,6 +13,9 @@ public interface UserMapper {
@Select("select * from db_account where username = #{text} or email = #{text}") @Select("select * from db_account where username = #{text} or email = #{text}")
Account findAccountByNameOrEmail(String 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}") @Select("select * from db_account where username = #{text} or email = #{text}")
AccountUser findAccountUserByNameOrEmail(String 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}") @Select("select * from db_account_info left join db_account on id = uid where id = #{uid}")
AccountInfo findInfoById(int 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 { public interface UserService {
boolean saveUserInfo(AccountInfo info); boolean saveUserInfo(AccountInfo info);
AccountInfo userInfo(int uid); 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.mapper.UserMapper;
import com.example.service.UserService; import com.example.service.UserService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@ -12,6 +13,8 @@ public class UserServiceImpl implements UserService {
@Resource @Resource
UserMapper mapper; UserMapper mapper;
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
@Override @Override
public boolean saveUserInfo(AccountInfo info) { public boolean saveUserInfo(AccountInfo info) {
Account account = mapper.findAccountByNameOrEmail(info.getUsername()); Account account = mapper.findAccountByNameOrEmail(info.getUsername());
@ -28,4 +31,25 @@ public class UserServiceImpl implements UserService {
public AccountInfo userInfo(int uid) { public AccountInfo userInfo(int uid) {
return mapper.findInfoById(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> <script setup>
import {reactive} from "vue"; import {onMounted, reactive, ref} from "vue";
import {Lock, Message, Select} from "@element-plus/icons-vue"; import {Lock, Message, Select} from "@element-plus/icons-vue";
import {get, logout, post} from "@/net";
import {ElMessage} from "element-plus";
const securityForm = reactive({ const securityForm = reactive({
email: '', email: null,
password_old: '', password_old: '',
password_new: '', password_new: '',
password_new_repeat: '', 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> </script>
<template> <template>
@ -15,35 +81,39 @@ const securityForm = reactive({
<div> <div>
<h1><el-icon><Message/></el-icon> 邮箱设置</h1> <h1><el-icon><Message/></el-icon> 邮箱设置</h1>
<el-form <el-form
ref="emailForm"
:rules="rules"
label-position="top" label-position="top"
label-width="100px" label-width="100px"
:model="securityForm" :model="securityForm"
style="max-width: 460px"> style="max-width: 460px">
<el-form-item label="邮箱地址"> <el-form-item label="邮箱地址" prop="email">
<el-input v-model="securityForm.email" /> <el-input v-model="securityForm.email" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-button type="success" :icon="Select">保存邮件地址</el-button> <el-button type="success" :icon="Select" @click="saveEmail">保存邮件地址</el-button>
</div> </div>
<el-divider/> <el-divider/>
<div> <div>
<h1><el-icon><Lock/></el-icon> 密码设置</h1> <h1><el-icon><Lock/></el-icon> 密码设置</h1>
<el-form <el-form
ref="passwordForm"
:rules="rules"
label-position="top" label-position="top"
label-width="100px" label-width="100px"
:model="securityForm" :model="securityForm"
style="max-width: 460px"> style="max-width: 460px">
<el-form-item label="原密码"> <el-form-item prop="password_old" label="原密码">
<el-input v-model="securityForm.password_old" /> <el-input type="password" show-password v-model="securityForm.password_old" />
</el-form-item> </el-form-item>
<el-form-item label="新密码"> <el-form-item prop="password_new" label="新密码">
<el-input v-model="securityForm.password_old" /> <el-input type="password" show-password v-model="securityForm.password_new" />
</el-form-item> </el-form-item>
<el-form-item label="重复新密码"> <el-form-item prop="password_new_repeat" label="重复新密码">
<el-input v-model="securityForm.password_old" /> <el-input type="password" show-password v-model="securityForm.password_new_repeat" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-button type="success" :icon="Select">修改密码</el-button> <el-button type="success" :icon="Select" @click="changePassword">修改密码</el-button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -41,4 +41,13 @@ function get(url, success, failure = defaultFailure, error = defaultError) {
}).catch(error) }).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> </template>
<script setup> <script setup>
import {get} from "@/net"; import {logout} from "@/net";
import {ElMessage} from "element-plus";
import router from "@/router"; import router from "@/router";
import {useStore} from "@/stores"; import {useStore} from "@/stores";
import {Document, Setting, Menu as IconMenu, Search, Back, Expand, Fold} from "@element-plus/icons-vue"; 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 store = useStore()
const isCollapse = ref(false) const isCollapse = ref(false)
const logout = () => {
get('/api/auth/logout', (message) => {
ElMessage.success(message)
store.auth.user = null
localStorage.removeItem('user')
router.push('/')
})
}
</script> </script>
<style scoped> <style scoped>