在前后端分离开发场景中,当自己的后台管理系统搭建好之后,想要使用uniapp或微信小程序对接Ruoyi框架时,做一款小程序,常会遇到401(Unauthorized)认证失败错误。
作者在网上搜索了大量资料,大多数都是因过滤器问题,需要放行权限或者是删掉注解等方案,经过逐一碰壁,发现问题并没有得到解决,最终通过令牌解决了。
本文将通过实际案例,深入剖析问题根源并提供系统化的解决方案。
一、问题现象还原、代码复现(uniapp请求为例)
//uniapp getinfo()请求用户信息方法
methods: {
getinfo(){
const requestTask = uni.request({
url: 'http://localhost:8080/system/user/list',
success: function(res) {
this.userlist= res.data.rows
console.log(res.data);
}
});
}
}
}
图1 调用getinfo()方法出现401报错
图2 直接使用浏览器请求接口,同样报错401
二、问题根源剖析
(1)认证令牌缺失(最常见)
举个例子:你要回家拿衣服,可是门锁了,你没拿钥匙,就进不去,无法拿到衣服
检测方法:在浏览器的控制台查看请求Headers是否含有令牌
异常表现:请求头缺少Authorization
字段
图3 请求头含有Authorization
令牌
图4 请求头没有Authorization
令牌(主要问题)
(2)Token失效情形
举个例子:你家里的是密码锁,你家的密码是一次性的,也就是说,使用完这个密码开门,把门关上后,密码会变为一个新的密码(令牌),所以会过期,又或者是你把家里的密码锁换了个新的(重启后端),密码也会变为一个新的。
也就是说:每一次登录或者重启ruoyi后端都会更新一个Authorization令牌,但在开发中,小程序不可能每次令牌变更都去更新一次Authorization令牌,所以这就需要我们去延长令牌的过期时间,避免令牌失效即可。
失效类型 | 特征描述 | 解决方案 |
---|---|---|
过期失效(常见) | Token解码后 | 重新登录获取新Token |
服务端主动失效(常见) | 服务器重启或强制退出 | 客户端重新认证 |
签名验证失败 | Token被篡改 | 检查Token生成/传输过程 |
(3)过滤器配置
Ruoyi框架的过滤器可能会拦截所有请求,包括静态资源请求,导致页面无法正常渲染。未正确放行静态资源如果过滤器没有正确配置静态资源的放行规则,可能会导致静态资源无法正常加载,进而影响前端页面的显示。
解决方法:在过滤器中明确放行静态资源路径。
/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html","/test/user","/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
}
三、解决步骤
第一步:获取令牌
运行项目,使用谷歌浏览器打开开发者界面Network界面后,点击登录,注意检查Network里的get方法,均可以找到令牌。
图5 获取Authorization
令牌
Authorization:
Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjQ4YTVlNGI5LTVhMzctNGYwNi05MTE3LTVkY2YyZTIwNDU2NiJ9.E5Yp1D3QcIfX3JviRaB_hJpUl-gbItdjwXrDETacQnH1xPaCMj8m1I3zXd3YrdCaCq3RhqSZMLJAq8WEBblGPw
第二步:修改令牌有效时间
打开ruoyi项目,找到token配置,修改令牌有效时间,改为久一点。
图6 Token配置
第三步:Uni-app请求添加请求头(令牌)
在Uni-app请求方法里面,添加请求头header,里面加入令牌参数。(一般来说,把获取到的令牌和请求方法代码,扔给AI,让他帮你加入请求头完善该请求方法即可实现)
getgames() {
const requestTask = uni.request({
url: 'http://193.112.157.48:8080/system/games/list',
header: {
'Authorization': 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjEwYjc3MWQ3LTJkM2UtNGI3MS1hMTIyLTdkM2Q5MDU1MjExNiJ9.z3CpkE2a1wHPUWq-Mgm28zuKEMjYWUK3nytiq6I3DS6Ye8HowFrBpHQeqW2e24LaZ9LMJE4HheguU_djcTo4Xw' // 你的完整token
},
success: (res) => { // 改用箭头函数保持this指向
if(res.statusCode === 200 && res.data.code === 200) {
this.gameslist = res.data.rows;
console.log('数据加载成功:', this.gameslist);
}
}
});
},
四、测试结果
(1)PostMan接口测试成功获取数据
图7 PostMan测试图
(2)Uni-app页面测试成功获取数据
图8 Uni-app请求头数据
图8 Uni-app请求结果