SpringSecurity认证与授权 || 实现自定义注解放行可匿名访问的接口
点关注不迷路,欢迎关注点赞评论!
最近在做一个关于认证与授权的功能模块,需要使用自定义注解完成匿名访问的接口放行功能。想必各位大佬都知道或者了解,springsecurity中三个configure方法
- configure(AuthenticationManagerBuilder auth)
- configure(HttpSecurity http)
- configure(WebSecurity web) 使用web.ignoring().antMatchers()方法;,可以实现不走 Spring Security 过滤器链
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login");
}
}
一、首先根据官网搭一个基础的环境,加入spring-boot-starter-security和spring-boot-starter-web依赖
修改pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
二、配置基础的SecurityConfig和WebMvcConfig以及登录页
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RequestMappingHandlerMapping handlerMapping;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.anyRequest()// 所有请求全部需要鉴权认证
.authenticated();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
在templates下添加login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
三、新建controller测试一把
@RestController
@RequestMapping
public class TestController {
@GetMapping("test")
public String test() {
return "直接过滤";
}
@GetMapping("test1")
public String test1() {
return "需要登录";
}
}
四、访问一下localhost:8080/test和test1接口,可以看到会跳转到登录页,说明我们的接口都是被拦截了的
五、现在我们通过 web.ignoring().antMatchers来把test放行,放行配置如下
@Override
public void configure(WebSecurity web) throws Exception {
// 此方法可以实现不走 Spring Security 过滤器链
web.ignoring().antMatchers(HttpMethod.GET, "/test");
}
六、再试下test接口访问,直接访问成功
而test1接口这个时候访问还是需要登录,说明我们的放行配置起到了作用
这个时候我们又要思考了,当项目越来越大,接口越来越多的时候怎么办呢?难道一直改代码?这样的方式恐怕不妥啊!问题不大,我们现在试一试用注解的方式来放行接口!
思路:把所有带这个注解的方法放行了不就行了吗
开始我们的操作
新建一个IgnoreAuth的注解
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface IgnoreAuth {
}
现在有了注解,怎么获取到所有带这个注解的方法呢?
spring提供了RequestMappingHandlerMapping类,可以帮助我们,来获得所有的URL 通过继承(这里就不给大家看源码了,感兴趣的可以自己去看),可以调用getHandlerMethods()来获取 先debug看下获取到的数据是什么样的
@Override
public void configure(WebSecurity web) throws Exception {
WebSecurity and = web.ignoring().and();
Map<RequestMappingInfo, HandlerMethod> handlerMethods =
handlerMapping.getHandlerMethods();
}
启动项目,看到handlerMethods中包含了key(请求的类型,请求url)value(具体的方法)
那现在有了所有的url方法,我们只需要将带有IgnoreAuth注解方法筛选出来,并且直接放行就行了
handlerMethods.forEach((info, method) -> {
// 带IgnoreAuth注解的方法直接放行
if (!Objects.isNull(method.getMethodAnnotation(IgnoreAuth.class))) {
// 根据请求类型做不同的处理
info.getMethodsCondition().getMethods().forEach(requestMethod -> {
switch (requestMethod) {
case GET:
// getPatternsCondition得到请求url数组,遍历处理
info.getPatternsCondition().getPatterns().forEach(pattern -> {
// 放行
and.ignoring().antMatchers(HttpMethod.GET, pattern);
});
break;
case POST:
info.getPatternsCondition().getPatterns().forEach(pattern -> {
and.ignoring().antMatchers(HttpMethod.POST, pattern);
});
break;
case DELETE:
info.getPatternsCondition().getPatterns().forEach(pattern -> {
and.ignoring().antMatchers(HttpMethod.DELETE, pattern);
});
break;
case PUT:
info.getPatternsCondition().getPatterns().forEach(pattern -> {
and.ignoring().antMatchers(HttpMethod.PUT, pattern);
});
break;
default:
break;
}
....
});
}
});
}
在test方法上打上我们的自定义注解IgnoreAuth
@IgnoreAuth
@GetMapping("test")
public String test() {
return "直接过滤";
}
当然,我这里只是其中一种实现方式,有哪儿不足的地方希望大家能在评论中指出来,谢谢大家!
写文真不容易,裂开!
转载自:https://juejin.cn/post/6980965300141293605