vue登陸攔截初體驗
也是第一次做vue前後端分離 用戶註冊 登錄攔截。項目已經搭建完成,下面分享一下搭建過程。
前端vue
1.login界面
2.註冊頁面
3.展示頁面
前臺頁面在element官網組件有詳細教程,這裏不再贅訴。主要還是後臺邏輯的實現不是嘛,哈哈。
邏輯分析
1.登陸
前臺vue將user對象傳到controller層,在service裏處理相關業務邏輯:
public ServerResponse login(User user , HttpSession session) {
//通過 用戶名獲取數據庫用戶
User userByName = userMapper.findUserByName(user.getName());
if(userByName==null){//無法通過用戶名獲取數據庫用戶,即用戶不存在
return ServerResponse.error("用戶不存在");
}
//通過數據庫用戶,獲得salt,然後將前臺的user的密碼加salt,然後md5兩次加密後和數據庫的密碼匹配
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + userByName.getSalt()));
if(!pwd.equals(userByName.getPassword())){//密碼匹配失敗
return ServerResponse.error("密碼錯誤");
}
//用戶名,密碼驗證成功,放入session中
session.setAttribute(SystemConstant.SESSION_KEY,userByName);
return ServerResponse.success();
}
2.註冊
註冊要驗證用戶名是否已存在,而且兩次密碼要相同,那麼很舒服的是element官網提供了我們驗證表單的代碼,要學會粘貼修改使用它即可。
當填寫用戶名是,有一個blur失去焦點事件,當觸發該事件時要調用後臺的根據名稱查詢用戶的方法。
public ServerResponse findUserByName(String name) {
User user = userMapper.findUserByName(name);
if(user == null){
return ServerResponse.success();
}
return ServerResponse.error();
}
當用戶名,密碼有問題時,表單不允許提交。然後將用戶名和驗證好的密碼傳入後臺。
public ServerResponse register(User user) {
//爲確保密碼安全性,給密碼加鹽
String salt = RandomStringUtils.randomAlphanumeric(20);
//爲確保密碼安全性,兩次MD5加密
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + salt));
user.setCreateDate(new Date());
//將鹽保存數據庫,只有對應的密碼和對應的鹽在一起纔會生成正確的密碼
user.setSalt(salt);
user.setPassword(pwd);
//新增用戶
userMapper.register(user);
return ServerResponse.success();
}
現有的坑
1.前後端分離,跨域問題?
2.前後端分離,cookies無法傳到後臺?
3.後端返回對應狀態碼時,前端怎麼把對應的狀態碼轉換跳轉到對應的頁面?
4.登錄攔截怎麼實現?
1.前後端分離,跨域問題?
通過後臺的攔截器方法,每當請求controller層方法時,都要走攔截器類,
在攔截器類解決跨域問題:
//跨域
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"http://localhost:9090");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"x-auth,content-type,mtoken");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"PUT,POST,DELETE,GET");
response.addHeader("Access-Control-Allow-credentials","true");
2.前後端分離,cookies無法傳到後臺?
z在前臺vue項目中 main.js 配置
axios.defaults.withCredentials = true
3.後端返回對應狀態碼時,前端怎麼把對應的狀態碼轉換跳轉到對應的頁面?
z在前臺vue項目中 main.js 配置 前臺攔截器:
對比狀態碼,通過location跳轉指定頁面。
4.登錄攔截怎麼實現?
首先我們要知道,有些方法是不能攔截的。所有要配置白名單,之前在登陸攔截的csdn裏已經介紹過一種方法,現在介紹另一種方法。
我自定義一個註解,
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ignore {
}
通過該註解可以選擇性過濾一些方法不攔截。
具體實現如下:
HandlerMethod handlerMethod= (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if(method.isAnnotationPresent(Ignore.class)){
return true;
}
只要在我寫過的方法上加上ignore了,方法就可以通通return true;
當我的session中沒有user時就意味着登陸失敗,那就要拋出一個我自定義的異常,返回給前臺一個code,通過判斷該code跳轉頁面。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//跨域
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"http://localhost:9090");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"x-auth,content-type,mtoken");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"PUT,POST,DELETE,GET");
response.addHeader("Access-Control-Allow-credentials","true");
//攔截器白名單
HandlerMethod handlerMethod= (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if(method.isAnnotationPresent(Ignore.class)){
return true;
}
User user = (User) request.getSession().getAttribute(SystemConstant.SESSION_KEY);
if(user != null){
return true;
}else{
throw new LoginException();
}
}
LoginException如下:
全局異常捕獲。
全部代碼
1.登陸vue:
<template>
<div style="margin-left: 35%; margin-top: 10%">
<el-form ref="user" :model="user" label-width="80px">
<el-form-item label="用戶名" style="width: 260px">
<el-input v-model="user.name"></el-input>
</el-form-item>
<el-form-item label="密碼" style="width: 260px">
<el-input type="password" v-model="user.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="onSubmit" style="margin-left: 20px; margin-top: 20px">登陸</el-button>
<el-link type="primary" style="margin-left: 20px;" href="/register">註冊</el-link>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "login",
data() {
return {
user: {
name: '123',
password: '123'
}
}
},
methods: {
onSubmit() {
var self = this;
this.$axios.post("http://localhost:8085/user/login.do",this.$qs.stringify(this.user)).then(function(res){
if(res.data.code==200){
self.$router.push('/product');
alert("登陸成功");
}else{
alert(res.data.data);
}
})
}
}
}
</script>
<style scoped>
</style>
2.註冊
<template>
<div style="margin-left: 35%; margin-top: 10%">
<el-form :model="user" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用戶名" prop="name" style="width:300px">
<el-input v-model.number="user.name"></el-input>
</el-form-item>
<el-form-item label="密碼" prop="password" style="width: 300px">
<el-input type="password" v-model="user.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="確認密碼" prop="checkPass" style="width: 300px">
<el-input type="password" v-model="user.checkPass" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "register",
data() {
var checkName = (rule, value, callback) => {
if (value === '') {
callback(new Error('請輸入用戶名'));
} else {
if (this.user.name !== '') {
var self = this;
this.$axios.post("http://localhost:8085/user/findUserByName.do",this.$qs.stringify({"name":this.user.name})).then(function(res){
if(res.data.code==200){
callback();
}else{
callback(new Error('用戶名已存在'));
}
})
}
}
};
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('請輸入密碼'));
} else {
if (this.user.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('請再次輸入密碼'));
} else if (value !== this.user.password) {
callback(new Error('兩次輸入密碼不一致!'));
} else {
callback();
}
};
return {
user: {
password: '',
checkPass: '',
name: ''
},
rules: {
pass: [
{ validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ validator: validatePass2, trigger: 'blur' }
],
name: [
{ validator: checkName, trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
var self = this;
this.$axios.post("http://localhost:8085/user/register.do",this.$qs.stringify(this.user)).then(function(res){
if(res.data.code==200){
self.$router.push('/');
alert("註冊成功");
}else{
alert("註冊失敗");
}
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style scoped>
</style>
3.service層
package com.fh.service.impl;
import com.fh.common.ServerResponse;
import com.fh.mapper.UserMapper;
import com.fh.model.User;
import com.fh.service.UserService;
import com.fh.util.MD5Util;
import com.fh.util.SystemConstant;
import com.qcloud.cos.utils.Md5Utils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public ServerResponse findUserByName(String name) {
User user = userMapper.findUserByName(name);
if(user == null){
return ServerResponse.success();
}
return ServerResponse.error();
}
public ServerResponse register(User user) {
//爲確保密碼安全性,給密碼加鹽
String salt = RandomStringUtils.randomAlphanumeric(20);
//爲確保密碼安全性,兩次MD5加密
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + salt));
user.setCreateDate(new Date());
//將鹽保存數據庫,只有對應的密碼和對應的鹽在一起纔會生成正確的密碼
user.setSalt(salt);
user.setPassword(pwd);
//新增用戶
userMapper.register(user);
return ServerResponse.success();
}
public ServerResponse login(User user , HttpSession session) {
//通過 用戶名獲取數據庫用戶
User userByName = userMapper.findUserByName(user.getName());
if(userByName==null){//無法通過用戶名獲取數據庫用戶,即用戶不存在
return ServerResponse.error("用戶不存在");
}
//通過數據庫用戶,獲得salt,然後將前臺的user的密碼加salt,然後md5兩次加密後和數據庫的密碼匹配
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + userByName.getSalt()));
if(!pwd.equals(userByName.getPassword())){//密碼匹配失敗
return ServerResponse.error("密碼錯誤");
}
//用戶名,密碼驗證成功,放入session中
session.setAttribute(SystemConstant.SESSION_KEY,userByName);
return ServerResponse.success();
}
}
4.全局異常處理
package com.fh.common;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(MyException.class)
public ServerResponse HandlerMyException (){
return ServerResponse.error();
}
@ExceptionHandler(LoginException.class)
public ServerResponse HandlerMyLoginException (){
return ServerResponse.error_login();
}
@ExceptionHandler(Exception.class)
public ServerResponse HandlerException (){
return ServerResponse.error();
}
}
5.自定義註解
package com.fh.common;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ignore {
}