Ruo-Yi 前后端分离如何不登录直接访问
问题
我们从若依项目的简介中可以知道,若依前后端分离系统采用了Spring Security作为权限校验框架,那么,如果我们想要不登录就可以访问某些页面应该怎么办?
分析
若依官网的解释:若依官网解释
有关spring security配置的东西若依框架都在SecurityConfig
类里面有写。找到configure()
方法,一般这个类里面会写。
这里的意思就是 permitAllUrl.getUrls()
集合 里面的路径因为后面设置了permitAll()
用户可以任意访问。所以我们要想某些路径不登陆就可以访问,其实只需要我们的不登陆像访问的路径在 permitAllUrl.getUrls()
集合里面即可。
# 若依原作者的注释* anyRequest | 匹配所有请求路径* access | SpringEl表达式结果为true时可以访问* anonymous | 匿名可以访问* denyAll | 用户不能访问* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问* hasRole | 如果有参数,参数表示角色,则其角色可以访问* permitAll | 用户可以任意访问* rememberMe | 允许通过remember-me登录的用户访问* authenticated | 用户登录后可访问
SecurityConfig
类里面有如下代码:
@Autowiredprivate PermitAllUrlProperties permitAllUrl;
permitAllUrl 是从这里注入进来的,想要知道怎么不登陆就可以访问,就必须分析类PermitAllUrlProperties
。
PermitAllUrlProperties 类
这个类有@Configuration
注解,代表这个类是已启动就被注册到 spring 容器里面。
package com.ruoyi.framework.config.properties;import com.ruoyi.common.annotation.Anonymous;import org.apache.commons.lang3.RegExUtils;import org.springframework.beans.BeansException;import org.springframework.beans.factory.InitializingBean;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.mvc.method.RequestMappingInfo;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Optional;import java.util.regex.Pattern;@Configurationpublic class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware{ private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); private ApplicationContext applicationContext; private List<String> urls = new ArrayList<>(); public String ASTERISK = "*"; @Override public void afterPropertiesSet() { RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); // HandlerMethod 类用于封装控制器方法信息,包含类信息、方法Method对象、参数、注解等信息,具体的接口请求是可以根据封装的信息调用具体的方法来执行业务逻辑; // RequestMappingInfo其实就是将我们熟悉的@RequestMapping注解的信息数据封装到了RequestMappingInfo POJO对象之中,然后和HandlerMethod做映射关系存入缓存之中; // 可以参考博客:https://blog.csdn.net/yaomingyang/article/details/107026595 Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); map.keySet().forEach(info -> { HandlerMethod handlerMethod = map.get(info); // 获取方法上边的注解 替代path variable 为 * // 从类或方法中查找某个 Anonymous 注解 Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); // 获取类上边的注解, 替代path variable 为 * Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); }); } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.applicationContext = context; } public List<String> getUrls() { return urls; } public void setUrls(List<String> urls) { this.urls = urls; }}
从上面的代码我们知道,只要我们给类上加 @Anonymous
注解,就可以将不登陆就可以访问的路径加入urls
集合里面。
@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Anonymous{}
测试
有了以上的分析,我们接下来测试一波。我们测试缓存监控页面,请求为
http://localhost/dev-api/monitor/cache
不加@Anonymous
注解之前:
测试结果:
加了 @Anonymous
注解之后:
测试结果,访问成功:
总结
在若依的系统里面,如果我们想要实现某一个接口不登陆就可以访问,只需要在方法上添加 @Anonymous
注解即可。
如果我们想要在我们的项目里面实现不登陆就可以访问,我们可以将若依的PermitAllUrlProperties
类和Anonymous
类拿过来,然后在SecurityConfig
spring security的配置类添加如下代码。
@Autowiredprivate PermitAllUrlProperties permitAllUrl;@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception{ // 注解标记允许匿名访问的url // 自定义@Anonymous注解添加到方法上可以实现跳过权限验证。 ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); // .... }
拓展:另一种方法基于配置文件
经过上面的分析,我们知道,实现不登陆就可以访问某些接口,若依系统采用的是注解的形式,其实我们还可以将不需要登陆的接口放入配置文件里面。
在 SecurityConfig
类改为
//@Autowired//private PermitAllUrlProperties permitAllUrl;@Resourceprivate IgnoreUrlsConfig ignoreUrlsConfig;@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception{ // 注解标记允许匿名访问的url // 自定义@Anonymous注解添加到方法上可以实现跳过权限验证。 ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); // permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); // 白名单 //不需要保护的资源路径允许访问 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); }
去掉原有的方式的类的注入,添加IgnoreUrlsConfig
添加类 IgnoreUrlsConfig
@Component@ConfigurationProperties(prefix = "secure.ignore")public class IgnoreUrlsConfig { private List<String> urls = new ArrayList<>(); public List<String> getUrls() { return urls; } public void setUrls(List<String> urls) { this.urls = urls; }}
在 application.yml
添加白名单
secure: ignore: urls: #安全路径白名单 - /monitor/cache
测试,还是测试之前的接口
http://localhost/dev-api/monitor/cache
记住,去掉权限的注解
测试成功:
以上的两种方法,我自己还是比较喜欢第二种,想要添加或者删除不需要登陆就可以访问的路径只需要修改配置文件即可,还不会污染业务代码。