前言
在前面的教程中,我们将后端的登录、认证、权限校验都已学会并经过测试,本章我们来让前端真正的对接上后端的登录接口。
下载Axios并封装API工具
在前端项目中,对于这种前后端分离的项目,api的请求主要是用axios库,执行如下命令即可
bash
npm install axios
然后我们在src目录下创建一个utils目录,再创建一个名为api.js的文件,加入如下代码,作为axios的封装工具
javascript
import axios from 'axios'
// 创建axios实例
const api = axios.create({
baseURL: 'http://localhost:8001', // 显式指定后端地址
timeout: 10000
})
/**
* 通用请求方法(支持.then()链式调用)
* @param {object} config - 请求配置
* @returns {Promise} 返回原始Promise对象
*/
export const request = ({ method, url, data, params }) => {
return api({ method, url, data, params })
.then(response => response.data) // 解构出data字段
.catch(error => {
console.error(`API请求失败 [${method?.toUpperCase()}] ${url}:`, error)
return Promise.reject(error) // 继续抛出错误
})
}
// 快捷方法(均返回Promise)
export const get = (url, params) => request({ method: 'get', url, params })
export const post = (url, data) => request({ method: 'post', url, data })
export const put = (url, data) => request({ method: 'put', url, data })
export const del = (url, params) => request({ method: 'delete', url, params })
/**
* //使用示例:发送登录请求
* import { post } from '@/utils/api'
*
* post("/auth/login",{
* "username":this.form.username,
* "password":this.form.password
* }).then(res=>{
* console.log(res)
* })
*
* **/
如图

修改登录真实请求
现在我们来到LoginView,将之前模拟登录的代码改为真正的登录请求,如图,幽络源这里将vue2中登录页面的登录方法修改为了利用我们的api.js进行了post请求"/auth/login"接口

测试登录出现跨域
我们启动前端和后端项目,随便填写用户名和密码,此时我们点击登录按钮,便会看到如下界面

打开浏览器控制台,可以看到这里出现了一条信息,如下:
Access to XMLHttpRequest at 'http://localhost:8001/auth/login' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这条提示表示 我们本地8080请求本地8001的接口被CORS 策略阻止了,CORS是HTTP的资源共享机制,而浏览器由于有同源策略的安全措施使不同源的请求被阻止。
对于这种跨域问题, 前端或者后端都可以通过配置来解决,幽络源这里提供最简单的方法,即后端处理跨域。
在后端项目的tenant模块中创建config包,作为配置包,然后创建CorsConfig类,具体代码如下
java
package com.tenant.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//允许跨域配置
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(false)
.maxAge(3600);
}
}
加上这段配置,跨域的问题就处理了。但是:由于我们这里用到了SpringSecurity,这里还有个配置需要修改,那就是我们对http的配置,修改为如下:对比之前的配置可以发现,这里我们将cors不在用disable()去禁止了,而是直接开启。
java
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/login").permitAll()
.anyRequest().authenticated();
//添加我们自己的过滤器,在登录前就加上token过滤器
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
}
另外幽络源这里有个提醒,若是要允许前端Cookie、Authorization登浏览器自动管理的这种凭据的话,那这里后端的配置中就需要将 allowCredentials 设置为 true,并且 allowedOrigins 也必须得指定明确的域名!
处理跨域后再次测试
如图,当我们加上跨域处理的配置后,重启项目测试登录,先测试错误的用户名和密码,可以看到成功的响应了提示信息:用户名或密码错误。

然后我们再传入正确的用户名和密码,如图可以看到后端成功的响应了登录成功信息,并且返回了JWT给前端

思考后续的接口访问
按照之前我们的登录与请求测试接口的流程,现在我们前段拿到了JWT,那么还需将JWT添加到前端请求头的"token"中去请求其他接口,否则会被后端我们自定义的的Token过滤器拦截掉。
再次封装axios请求
由于后续我们每次请求都需要为请求头加上"token",不可能每次都手动去添加,因此这里幽络源需要修改axios的封装方式,并添加上请求和响应的拦截器,让每次前端请求时,都自动添加上"token"。
将我们的 api.js 修改为如下,注意:这里保留了上面的快捷封装方式,可以不使用,但还是值得去学习的。
java
import axios from 'axios'
// 创建axios实例
const api = axios.create({
baseURL: 'http://localhost:8001',
timeout: 10000
})
// 请求拦截器:自动添加Token
api.interceptors.request.use(config => {
const token = localStorage.getItem('jwt_token')
if (token) {
config.headers['token'] = token // 统一使用'token'作为键名
}
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截器
api.interceptors.response.use(
response => response.data, // 直接返回data
error => {
if (error.response?.status === 401) {
localStorage.removeItem('jwt_token')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default api //直接导出api实例
/**
* 封装通用请求方法
* @param {object} config - { method, url, data, params }
* @returns {Promise} 返回处理后的Promise
*/
export const service = ({ method, url, data, params }) => {
return api({ method, url, data, params }) // 直接返回api实例的调用
}
// 快捷方法(可选)
export const get = (url, params) => service({ method: 'get', url, params })
export const post = (url, data) => service({ method: 'post', url, data })
export const put = (url, data) => service({ method: 'put', url, data })
export const del = (url, params) => service({ method: 'delete', url, params })
/**
* //快捷方法使用示例:发送登录请求
* import { post } from '@/utils/api'
*
* post("/auth/login",{
* "username":this.form.username,
* "password":this.form.password
* }).then(res=>{
* console.log(res)
* })
* **/
封装登录请求
修改了axios请求封装后,我们来将登录请求也封装了,后续接口会有很多,因此幽络源这里会将请求的封装统一放在src下的名为api的目录,然后再在api的目录下创建名为tenant的目录,再创建auth.js对应后端的auth接口,内容与图如下
javascript
import request from '@/utils/api.js';
export function login(data) {
return request({
url: '/auth/login',
method: 'post',
data
});
}

继续修改登录逻辑
上面我们因为考虑到请求头需要携带token,加上了请求拦截器,进而进一步封装了axios,并封装了登录请求,这里登录的逻辑也得修改,代码与图如下
javascript
handleLogin() {
// 验证所有字段
this.validate('username')
this.validate('password')
// 如果有错误,不提交
if (this.errors.username || this.errors.password) {
return
}
this.errorMessage = ''
//发送登录请求
login({
"username":this.form.username,
"password":this.form.password
}).then(res=>{
if (res.code===0){ //登录失败的处理
this.$message({
message: res.message,
type: 'warning'
});
}else{ //登录成功的处理
localStorage.setItem('jwt_token', res.data);
this.$message.success('登录成功');
this.$router.push("/")
}
})
},

再次测试登录功能
如图,分别测试错误的账号密码和正确的账号密码,结果如下,非常成功


权限的增删查改完善
权限的增删查改就比较简单了,直接新疆controller,写对应的service,调用mybatisplus自带的方法即可,需要注意的是权限的查询在前端是有父子分级的,这个逻辑幽络源这里准备在后端做,当然前端也能做,如图

后端权限递归查询修改与前端权限查询完善
这里权限的树状查询就像是父子菜单一样,后期可能不止有两层级,因此这里得递归的去操作,首先修改Permission类为其添加子节点,如下
java
@Data
@TableName("tbs_permission")
public class Permission {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("name")
private String name;
@TableField("code")
private String code;
@TableField("type")
private Integer type;
@TableField("parent_id")
private Long parentId;
@TableField("path")
private String path;
@TableField("icon")
private String icon;
@TableField("sort")
private Integer sort;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(exist = false)
private List<Permission> children; // 子节点
}
权限的查询修改为递归构建,如下
java
@Override
public ResResult<?> select(String name, String code, Integer type) {
LambdaQueryWrapper<Permission> eq = new LambdaQueryWrapper<Permission>()
.orderByAsc(Permission::getSort)
.like(StringUtils.isNotBlank(name), Permission::getName, name)
.like(StringUtils.isNotBlank(code), Permission::getCode, code)
.eq(type != null, Permission::getType, type);
List<Permission> permissions = permissionMapper.selectList(eq);
List<Permission> permissionsTree = buildTree(permissions, null);
return ResResult.success(permissionsTree,"权限查询成功");
}
// 递归构建树
private List<Permission> buildTree(List<Permission> allPermissions, Long parentId) {
return allPermissions.stream()
.filter(p -> Objects.equals(p.getParentId(), parentId))
.peek(p -> p.setChildren(buildTree(allPermissions, p.getId())))
.collect(Collectors.toList());
}
前端项目新建permission.js,封装查询请求,并应用到查询界面,代码与图如下
permission.js
javascript
import request from '@/utils/api.js';
export function selectPermission(name,code,type) {
return request({
url: '/permission/select?name='+name+'&code='+code+'&type='+type,
method: 'get'
});
}
PermissionView.vue
javascript
//获取并且处理权限
getAndHandlePermission(){
selectPermission(this.listQuery.name,this.listQuery.code,this.listQuery.type).then(res=>{
if (res.code===1){ //请求成功,处理权限分级并加入到list
this.list=res.data;
}else { //请求失败的处理
this.$message({
message: res.message,
type: 'warning'
});
}
})
}

除此之外,我们还需对权限相关接口加上鉴权注解,一般来说,只允许超管对其进行增删查改,你可以通过角色编码去鉴权,也可以通过权限编码去鉴权,这里幽络源就直接用我们先前所讲的@PreAuthorize("hasAuthority(")")做鉴权即可,如图

测试权限的查询与鉴权成败
如图,幽络源登录超管的账号来到系统管理中的权限管理处,可以看到这里直接就加载了树状的权限列表

幽络源继续使用 非超管用户登录 系统,然后来到权限管理界面,结果如下显示了403拒绝访问接口,因此我们的鉴权也是成功的,当然,直接给出这样一个红色界面对用户来说是看不懂的不合理的,后续幽络源会继续整合动态菜单以及处理403的响应。

源码
https://pan.baidu.com/s/1Pxb5OEL0feh_Hg12Ye16tg?pwd=3a15
结语
如上为幽络源的8、幽络源微服务项目实战:vue2对接登录接口与跨域同源策略的处理+前端axios封装+权限的递归查询增删改+鉴权测试教程