目录
1、权限处理后端接口
1.1 SpringSecurityConfig
1.2 在控制器中加入权限控制
2、前端页面按钮权限判断
2.1 保存权限字段
2.2 编写按钮权限判断
2.3 引入按钮权限判断脚本
2.4 按钮权限判断脚本使用
3、token过期处理
3.1 编写Store代码
3.2 编写刷新token新代码
3.3 编写token过期方法
3.4 编写请求拦截
1、权限处理后端接口
1.1 SpringSecurityConfig
@Configuration
@EnableWebSecurity
//开启权限注解控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
1.2 在控制器中加入权限控制
package com.cizhu.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cizhu.dto.UserRoleDTO;
import com.cizhu.entity.Role;
import com.cizhu.entity.User;
import com.cizhu.service.IRoleService;
import com.cizhu.service.IUserService;
import com.cizhu.utils.Result;
import com.cizhu.vo.query.RoleQueryVo;
import com.cizhu.vo.query.UserQueryVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author cizhu
* @since 2023-12-14
*/
@RestController
@RequestMapping("/api/user")
public class UserController {
@Resource
private IUserService userService;
@Resource
private IRoleService roleService ;
@Resource
private PasswordEncoder passwordEncoder;
@GetMapping("/listAll")
public Result findAll(){
List<User> userList = userService.list();
return Result.ok(userList);
}
/**
* 查询用户列表
* @param userQueryVo
* @return
*/
@GetMapping("/list")
public Result list(UserQueryVo userQueryVo) {
//创建分页信息
IPage<User> page = new Page<User>(userQueryVo.getPageNo(), userQueryVo.getPageSize());
//调用分页查询方法
userService.findUserListByPage(page, userQueryVo);
//返回数据
return Result.ok(page);
}
/**
* 添加用户
*
* @param user
* @return
*/
@PostMapping("/add")
@PreAuthorize("hasAuthority('sys:user:add')")
public Result add(@RequestBody User user) {
//查询用户
User item = userService.findUserByUserName(user.getUsername());
//判断对象是否为空
if (item != null) {
return Result.error().message("该登录名称已被使用,请重新输入!");
}
//密码加密
user.setPassword(passwordEncoder.encode(user.getPassword()));
//调用保存用户信息的方法
if(userService.save(user)){
return Result.ok().message("用户添加成功");
}
return Result.error().message("用户添加失败");
}
/**
* 修改用户
*
* @param user
* @return
*/
@PutMapping("/update")
@PreAuthorize("hasAuthority('sys:user:edit')")
public Result update(@RequestBody User user) {
//查询用户
User item = userService.findUserByUserName(user.getUsername());
//判断对象是否为空,且查询到的用户ID不等于当前编辑的用户ID,表示该名称被占用
if (item != null && item.getId() != user.getId()) {
return Result.error().message("登录名称已被占用!");
}
//调用修改用户信息的方法
if(userService.updateById(user)){
return Result.ok().message("用户修改成功");
}
return Result.error().message("用户修改失败");
}
/**
* 删除用户
* @param id
* @return
*/
@DeleteMapping("/delete/{id}")
@PreAuthorize("hasAuthority('sys:user:delete')")
public Result delete(@PathVariable Long id) {
//调用删除用户信息的方法
if(userService.deleteById(id)){
return Result.ok().message("用户删除成功");
}
return Result.error().message("用户删除失败");
}
/**
* 获取分配角色列表
* @param roleQueryVo
* @return
*/
@GetMapping("/getRoleListForAssign")
@PreAuthorize("hasAuthority('sys:user:assign')")
public Result getRoleListForAssign(RoleQueryVo roleQueryVo){
//创建分页对象
IPage<Role> page = new Page<Role>(roleQueryVo.getPageNo(), roleQueryVo.getPageSize());
//调用查询方法
roleService.findRoleListByUserId(page,roleQueryVo);
//返回数据
return Result.ok(page);
}
/**
* 根据用户ID查询该用户拥有的角色列表
* @param userId
* @return
*/
@GetMapping("/getRoleByUserId/{userId}")
@PreAuthorize("hasAuthority('sys:user:assign')")
public Result getRoleByUserId(@PathVariable Long userId){
//调用根据用户ID查询该用户拥有的角色ID的方法
List<Long> roleIds = roleService.findRoleIdByUserId(userId);
return Result.ok(roleIds);
}
/**
* 分配角色
* @param userRoleDTO
* @return
*/
@PostMapping("/saveUserRole")
@PreAuthorize("hasAuthority('sys:user:assign')")
public Result saveUserRole(@RequestBody UserRoleDTO userRoleDTO){
if (userService.saveUserRole(userRoleDTO.getUserId(),
userRoleDTO.getRoleIds())) {
return Result.ok().message("角色分配成功");
}
return Result.error().message("角色分配失败");
}
}
2、前端页面按钮权限判断
2.1 保存权限字段
2.2 编写按钮权限判断
2.3 引入按钮权限判断脚本
2.4 按钮权限判断脚本使用
3、token过期处理
3.1 编写Store代码
utils/auth.js
//定义token过期时间的key
const timeKey = "expireTime"
/**
* 设置token过期时间
* @returns
*/
export function setTokenTime(time){
return sessionStorage.setItem(timeKey,time);
}
/**
* 获取token过期时间
* @returns
*/
export function getTokenTime(){
return sessionStorage.getItem(timeKey);
}
/**
* 清空token过期时间
* @returns
*/
export function removeTokenTime(){
return sessionStorage.setItem(timeKey,0);
}
import { getToken, setToken, removeToken, setTokenTime } from '@/utils/auth'
store/user.js
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken, setTokenTime } from '@/utils/auth'
import router, { resetRouter } from '@/router'
const state = {
token: getToken(), // 获取token信息
name: '',
avatar: '',
introduction: '',
roles: []
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_USERUID: (state, userId) => {
state.userId = userId
},
}
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { token, expireTime } = response
commit('SET_TOKEN', token)
setToken(token)
// 设置token过期时间
setTokenTime(expireTime)
resolve()
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
// 从后端反馈的data数据中解构出用户相关信息
const { roles, name, avatar, introduction, id } = data
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
commit('SET_USERUID', id)
//将权限字段保存到sessionStorage中
sessionStorage.setItem("codeList",JSON.stringify(roles));
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state, dispatch }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resetRouter()
// reset visited views and cached views
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
},
// dynamically modify permissions
async changeRoles({ commit, dispatch }, role) {
const token = role + '-token'
commit('SET_TOKEN', token)
setToken(token)
const { roles } = await dispatch('getInfo')
resetRouter()
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true })
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
3.2 编写刷新token新代码
3.3 编写token过期方法
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
//定义token过期时间的key
const timeKey = "expireTime"
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
/**
* 清空sessionStorage
*/
export function clearStorage(){
return sessionStorage.clear();
}
/**
* 设置token过期时间
* @returns
*/
export function setTokenTime(time){
return sessionStorage.setItem(timeKey,time);
}
/**
* 获取token过期时间
* @returns
*/
export function getTokenTime(){
return sessionStorage.getItem(timeKey);
}
/**
* 清空token过期时间
* @returns
*/
export function removeTokenTime(){
return sessionStorage.setItem(timeKey,0);
}
3.4 编写请求拦截
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
// 导入auth.js脚步
import { getToken, setToken, clearStorage, setTokenTime, getTokenTime, removeTokenTime } from '@/utils/auth'
// 导入qs依赖(该依赖用于将参数进行序列化,如:/user?username=xxx&password=&&&)
import qs from 'qs'
//导入刷新token的api脚本
import { refreshToken } from '@/api/user'
// 创建axios异步请求实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 50000 // request timeout
})
/**
* 刷新token
*/
function refreshTokenInfo(){
//设置请求参数
let param = {
token:getToken()
}
return refreshToken(param).then(res=>res);
}
//定义变量,标识是否刷新token
let isRefresh = false
// 请求拦截器
service.interceptors.request.use(
config => {
// 获取系统当前时间
let currentTime = new Date().getTime()
// 获取token过期时间
let expireTime = getTokenTime()
if (expireTime > 0){
// 计算过期时间
let min = (expireTime - currentTime)/1000/60
// 如果token离过期时间在10分钟之内,则刷新token
if (min < 10){
if (!isRefresh){
// 修改刷新状态
isRefresh = true
return refreshTokenInfo().then(res => {
if (res.success){
// 设置新的token和新的过期时间
setToken(res.data.token)
setTokenTime(res.data.expireTime)
// 将新的token添加到页header中
config.headers.token = getToken()
}
// 返回配置
return config
}).catch(error => {
}).finally(() => {
// 修改刷新token状态
isRefresh = false
})
}
}
}
// 判断store中是否存在token
if (store.getters.token) {
// 读取token信息,并添加到header信息中
config.headers['token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
// 清空sessionStorage
clearStorage()
// 清空token过期时间
removeTokenTime()
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
// 返回状态码不是200,提示错误
if (res.code !== 200) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('用户登录信息过期,请重新登录!', '系统提示', {
confirmButtonText: '登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
// 清空sessionStorage
clearStorage()
// 清空token过期时间
removeTokenTime()
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
// 清空sessionStorage
clearStorage()
// 清空token过期时间
removeTokenTime()
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
//请求方法
const http = {
// post请求提交
post(url, params) {
return service.post(url, params, {
transformRequest: [(params) => {
return JSON.stringify(params)
}],
headers: {
'Content-Type': 'application/json'
}
})
},
// put请求
put(url, params) {
return service.put(url, params, {
transformRequest: [(params) => {
return JSON.stringify(params)
}],
headers: {
'Content-Type': 'application/json'
}
})
},
// get请求
get(url, params) {
return service.get(url, {
params: params,
paramsSerializer: (params) => {
return qs.stringify(params)
}
})
},
// rest风格的Get请求
getRestApi(url, params) {
let _params
if (Object.is(params, undefined || null)) {
_params = ''
} else {
_params = '/'
for (const key in params) {
//console.log(key)
//console.log(params[key])
if (params.hasOwnProperty(key) && params[key] !== null && params[key] !== '') {
_params += `${params[key]}/`
}
}
_params = _params.substr(0, _params.length - 1)
}
//console.log(_params)
if (_params) {
return service.get(`${url}${_params}`)
} else {
return service.get(url)
}
},
// delete请求
delete(url, params) {
let _params
if (Object.is(params, undefined || null)) {
_params = ''
} else {
_params = '/'
for (const key in params) {
// eslint-disable-next-line no-prototype-builtins
if (params.hasOwnProperty(key) && params[key] !== null && params[key] !== '') {
_params += `${params[key]}/`
}
}
_params = _params.substr(0, _params.length - 1)
}
if (_params) {
return service.delete(`${url}${_params}`).catch(err => {
message.error(err.msg)
return Promise.reject(err)
})
} else {
return service.delete(url).catch(err => {
message.error(err.msg)
return Promise.reject(err)
})
}
},
upload(url, params) {
return service.post(url, params, {
headers: {'Content-Type': 'multipart/form-data'}
})
},
login(url, params) {
return service.post(url, params, {
transformRequest: [(params) => {
return qs.stringify(params)
}],
headers: { 'Content-Type': 'application/x-www-form-urlencoded'}
})
}
}
export default http