本節(jié)代碼開源地址
用戶登錄后端(JWT)
0.JwtUtil
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
public class JwtUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
public static final long EXPIRATION_TIME = 3600_000_000L; // 1000 hour
public static final String SECRET = "ThisIsASecret";//please change to your own encryption secret.
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String USER_NAME = "userName";
public static String generateToken(String userId) {
HashMap<String, Object> map = new HashMap<>();
//you can put any data in the map
map.put(USER_NAME, userId);
String jwt = Jwts.builder()
.setClaims(map)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
return jwt; //jwt前面一般都會加Bearer
}
public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
try {
Map<String, Object> body = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
return new CustomHttpServletRequest(request, body);
} catch (Exception e) {
logger.info(e.getMessage());
throw new TokenValidationException(e.getMessage());
}
} else {
throw new TokenValidationException("Missing token");
}
}
public static class CustomHttpServletRequest extends HttpServletRequestWrapper {
private Map<String, String> claims;
public CustomHttpServletRequest(HttpServletRequest request, Map<String, ?> claims) {
super(request);
this.claims = new HashMap<>();
claims.forEach((k, v) -> this.claims.put(k, String.valueOf(v)));
}
@Override
public Enumeration<String> getHeaders(String name) {
if (claims != null && claims.containsKey(name)) {
return Collections.enumeration(Arrays.asList(claims.get(name)));
}
return super.getHeaders(name);
}
}
static class TokenValidationException extends RuntimeException {
public TokenValidationException(String msg) {
super(msg);
}
}
}
1.dto
@Data
public class LoginDTO {
@NotBlank(message = "用戶名不能為空")
@Size(min = 2, max = 15, message = "登錄用戶名長度在2-15")
private String username;
@NotBlank(message = "密碼不能為空")
@Size(min = 6, max = 20, message = "登錄密碼長度在6-20")
private String password;
private Boolean rememberMe;
}
2.UmsUserController
@PostMapping("/login")
public ApiResult login(@Valid @RequestBody LoginDTO loginDTO) {
Map<String, String> map = umsUserService.login(loginDTO);
return ApiResult.success(map, "登錄成功");
}
3.UmsUserService
/**
* 登錄
*
* @param loginDTO
* @return
*/
public Map<String, String> login(LoginDTO loginDTO) {
// 郵箱或用戶名是否存在
String loginUserName = loginDTO.getUsername();
UmsUser umsUser = this.getOne(new LambdaQueryWrapper<UmsUser>()
.eq(UmsUser::getUsername, loginUserName)
.or()
.eq(UmsUser::getEmail, loginUserName));
if (ObjectUtils.isEmpty(umsUser)) {
ApiAsserts.fail("用戶名或郵箱不存在");
}
// 校驗(yàn)密碼
if (!MD5Utils.getPwd(loginDTO.getPassword()).equals(umsUser.getPassword())) {
ApiAsserts.fail("密碼錯誤,請重新輸入");
}
// 生成 token
String token = JwtUtil.generateToken(loginUserName);
HashMap<String, String> map = new HashMap<>(16);
map.put("token",token);
return map;
}
用戶登錄前端
1.安裝js-cookie
存放瀏覽器的Cookies
yarn add js-cookie
2.src/util創(chuàng)建auth.js
import Cookies from 'js-cookie'
// 存放token
const uToken = 'u_token'
// 存放白天還是黑夜模式
const darkMode = 'dark_mode';
// 獲取Token
export function getToken() {
return Cookies.get(uToken);
}
// 設(shè)置Token,1天,與后端同步
export function setToken(token) {
return Cookies.set(uToken, token, {expires: 1})
}
// 刪除Token
export function removeToken() {
return Cookies.remove(uToken)
}
export function removeAll() {
return Cookies.Cookies.removeAll()
}
export function setDarkMode(mode) {
return Cookies.set(darkMode, mode, {expires: 365})
}
export function getDarkMode() {
return !(undefined === Cookies.get(darkMode) || 'false' === Cookies.get(darkMode));
}
3.登錄路由
src/router/index.js添加路由
,{
path: '/login',
name: 'login',
component: () => import('@/views/auth/login'),
meta: {title: '登錄'}
}
4./views/auth創(chuàng)建login.vue
<template>
<div class="columns py-6">
<div class="column is-half is-offset-one-quarter">
<el-card shadow="never">
<div slot="header" class="has-text-centered has-text-weight-bold">
用戶登錄
</div>
<div>
<el-form
v-loading="loading"
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="賬號" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密碼" prop="pass">
<el-input
type="password"
v-model="ruleForm.pass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="記住" prop="delivery">
<el-switch v-model="ruleForm.rememberMe"></el-switch>
</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>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
redirect: undefined,
loading: false,
ruleForm: {
name: "",
pass: "",
rememberMe: true,
},
rules: {
name: [
{ required: true, message: "請輸入賬號", trigger: "blur" },
{
min: 2,
max: 15,
message: "長度在 2 到 15 個字符",
trigger: "blur",
},
],
pass: [
{ required: true, message: "請輸入密碼", trigger: "blur" },
{
min: 6,
max: 20,
message: "長度在 6 到 20 個字符",
trigger: "blur",
},
],
},
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.loading = true;
this.$store
.dispatch("user/login", this.ruleForm)
.then(() => {
this.$message({
message: "恭喜你腿宰,登錄成功",
type: "success",
duration: 2000,
});
setTimeout(() => {
this.loading = false;
this.$router.push({ path: this.redirect || "/" });
}, 0.1 * 1000);
})
.catch(() => {
this.loading = false;
});
} else {
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
<style scoped>
</style>
5.API請求地址
// 登錄
export function login(data) {
return request({
url: '/auth/user/login',
method: 'post',
data
})
}
6.src/store創(chuàng)建modules/user.js
import { getUserInfo, login, logout } from "@/api/auth/auth";
import { getToken, setToken, removeToken } from "@/utils/auth";
const state = {
token: getToken(), // token
user: "", // 用戶對象
};
const mutations = {
SET_TOKEN_STATE: (state, token) => {
state.token = token;
},
SET_USER_STATE: (state, user) => {
state.user = user;
},
};
const actions = {
// 用戶登錄
login({ commit }, userInfo) {
console.log(userInfo);
const { name, pass, rememberMe } = userInfo;
return new Promise((resolve, reject) => {
login({ username: name.trim(), password: pass, rememberMe: rememberMe })
.then((response) => {
const { data } = response;
commit("SET_TOKEN_STATE", data.token);
setToken(data.token);
resolve();
})
.catch((error) => {
reject(error);
});
});
},
// 獲取用戶信息
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getUserInfo()
.then((response) => {
const { data } = response;
if (!data) {
commit("SET_TOKEN_STATE", "");
commit("SET_USER_STATE", "");
removeToken();
resolve();
reject("Verification failed, please Login again.");
}
commit("SET_USER_STATE", data);
resolve(data);
})
.catch((error) => {
reject(error);
});
});
},
// 注銷
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token)
.then((response) => {
console.log(response);
commit("SET_TOKEN_STATE", "");
commit("SET_USER_STATE", "");
removeToken();
resolve();
})
.catch((error) => {
reject(error);
});
});
},
};
export default {
namespaced: true,
state,
mutations,
actions,
};
7.src/store的index.js
index.js的全部內(nèi)容
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user
}
})
export default store
8.測試頁面
輸入正確的用戶名和密碼之后宴猾,在Cookies中會生成token
登錄歡迎側(cè)邊欄
1.veiws/card/LoginWelcome.vue
復(fù)制一下內(nèi)容替換
<template>
<el-card class="box-card" shadow="never">
<div slot="header">
<span>?? 發(fā)帖</span>
</div>
<div v-if="token != null && token !== ''" class="has-text-centered">
<b-button type="is-danger" tag="router-link" :to="{path:'/post/create'}" outlined>? 發(fā)表想法</b-button>
</div>
<div v-else class="has-text-centered">
<b-button type="is-primary" tag="router-link" :to="{path:'/register'}" outlined>馬上入駐</b-button>
<b-button type="is-danger" tag="router-link" :to="{path:'/login'}" outlined class="ml-2"> 社區(qū)登入</b-button>
</div>
</el-card>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'LoginWelcome',
computed: {
...mapGetters([
'token'
])
}
}
</script>
<style scoped>
</style>
2.src/store/創(chuàng)建getters.js
const getters = {
token: state => state.user.token, // token
user: state => state.user.user, // 用戶對象
}
export default getters