Commit 689171c18dca9304df6edd9efd1a90a1a9b35a1e
1 parent
0439a550
增加 @PreAuthenticated 注解,实现登陆的拦截
Showing
9 changed files
with
155 additions
and
21 deletions
pom.xml
| @@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
| 19 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 19 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 20 | 20 | ||
| 21 | <properties> | 21 | <properties> |
| 22 | - <revision>1.0.0-snapshot</revision> | 22 | + <revision>1.1.0-snapshot</revision> |
| 23 | <!-- Maven 相关 --> | 23 | <!-- Maven 相关 --> |
| 24 | <java.version>1.8</java.version> | 24 | <java.version>1.8</java.version> |
| 25 | <maven.compiler.source>${java.version}</maven.compiler.source> | 25 | <maven.compiler.source>${java.version}</maven.compiler.source> |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.adminserver.framework.security; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.web.config.WebProperties; | ||
| 4 | +import org.springframework.beans.factory.annotation.Value; | ||
| 5 | +import org.springframework.context.annotation.Bean; | ||
| 6 | +import org.springframework.context.annotation.Configuration; | ||
| 7 | +import org.springframework.security.config.Customizer; | ||
| 8 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
| 9 | +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; | ||
| 10 | + | ||
| 11 | +import javax.annotation.Resource; | ||
| 12 | + | ||
| 13 | +@Configuration | ||
| 14 | +public class SecurityConfiguration { | ||
| 15 | + | ||
| 16 | + @Resource | ||
| 17 | + private WebProperties webProperties; | ||
| 18 | + | ||
| 19 | + @Value("${spring.boot.admin.context-path:''}") | ||
| 20 | + private String adminSeverContextPath; | ||
| 21 | + | ||
| 22 | + @Bean | ||
| 23 | + public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() { | ||
| 24 | + return registry -> { | ||
| 25 | + // 通用的接口,可匿名访问 TODO 芋艿:需要抽象出去 | ||
| 26 | + registry.antMatchers(api("/system/captcha/**")).anonymous(); | ||
| 27 | + // Spring Boot Admin Server 的安全配置 TODO 芋艿:需要抽象出去 | ||
| 28 | + registry.antMatchers(adminSeverContextPath).anonymous() | ||
| 29 | + .antMatchers(adminSeverContextPath + "/**").anonymous(); | ||
| 30 | + // 短信回调 API | ||
| 31 | + registry.antMatchers(api("/system/sms/callback/**")).anonymous(); | ||
| 32 | + }; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + private String api(String url) { | ||
| 36 | + return webProperties.getApiPrefix() + url; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | +} |
yudao-framework/yudao-spring-boot-starter-security/pom.xml
| @@ -19,7 +19,12 @@ | @@ -19,7 +19,12 @@ | ||
| 19 | <dependency> | 19 | <dependency> |
| 20 | <groupId>cn.iocoder.boot</groupId> | 20 | <groupId>cn.iocoder.boot</groupId> |
| 21 | <artifactId>yudao-common</artifactId> | 21 | <artifactId>yudao-common</artifactId> |
| 22 | - <version>${revision}</version> | 22 | + </dependency> |
| 23 | + | ||
| 24 | + <!-- Spring 核心 --> | ||
| 25 | + <dependency> | ||
| 26 | + <groupId>org.springframework.boot</groupId> | ||
| 27 | + <artifactId>spring-boot-starter-aop</artifactId> | ||
| 23 | </dependency> | 28 | </dependency> |
| 24 | 29 | ||
| 25 | <!-- Web 相关 --> | 30 | <!-- Web 相关 --> |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
| 1 | package cn.iocoder.yudao.framework.security.config; | 1 | package cn.iocoder.yudao.framework.security.config; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; | ||
| 3 | import cn.iocoder.yudao.framework.security.core.filter.JwtAuthenticationTokenFilter; | 4 | import cn.iocoder.yudao.framework.security.core.filter.JwtAuthenticationTokenFilter; |
| 4 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; | 5 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; |
| 5 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; | 6 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; |
| @@ -33,6 +34,14 @@ public class YudaoSecurityAutoConfiguration { | @@ -33,6 +34,14 @@ public class YudaoSecurityAutoConfiguration { | ||
| 33 | private SecurityProperties securityProperties; | 34 | private SecurityProperties securityProperties; |
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| 37 | + * 处理用户未登陆拦截的切面的 Bean | ||
| 38 | + */ | ||
| 39 | + @Bean | ||
| 40 | + public PreAuthenticatedAspect preAuthenticatedAspect() { | ||
| 41 | + return new PreAuthenticatedAspect(); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /** | ||
| 36 | * 认证失败处理类 Bean | 45 | * 认证失败处理类 Bean |
| 37 | */ | 46 | */ |
| 38 | @Bean | 47 | @Bean |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
| @@ -14,10 +14,12 @@ import org.springframework.context.annotation.Bean; | @@ -14,10 +14,12 @@ import org.springframework.context.annotation.Bean; | ||
| 14 | import org.springframework.context.annotation.Configuration; | 14 | import org.springframework.context.annotation.Configuration; |
| 15 | import org.springframework.http.HttpMethod; | 15 | import org.springframework.http.HttpMethod; |
| 16 | import org.springframework.security.authentication.AuthenticationManager; | 16 | import org.springframework.security.authentication.AuthenticationManager; |
| 17 | +import org.springframework.security.config.Customizer; | ||
| 17 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | 18 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; |
| 18 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; | 19 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
| 19 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; | 20 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
| 20 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | 21 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
| 22 | +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; | ||
| 21 | import org.springframework.security.config.http.SessionCreationPolicy; | 23 | import org.springframework.security.config.http.SessionCreationPolicy; |
| 22 | import org.springframework.security.core.userdetails.UserDetailsService; | 24 | import org.springframework.security.core.userdetails.UserDetailsService; |
| 23 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | 25 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| @@ -41,9 +43,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -41,9 +43,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 41 | @Resource | 43 | @Resource |
| 42 | private WebProperties webProperties; | 44 | private WebProperties webProperties; |
| 43 | 45 | ||
| 44 | - @Value("${spring.boot.admin.context-path:''}") | ||
| 45 | - private String adminSeverContextPath; | ||
| 46 | - | ||
| 47 | /** | 46 | /** |
| 48 | * 自定义用户【认证】逻辑 | 47 | * 自定义用户【认证】逻辑 |
| 49 | */ | 48 | */ |
| @@ -74,6 +73,13 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -74,6 +73,13 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 74 | */ | 73 | */ |
| 75 | @Resource | 74 | @Resource |
| 76 | private JwtAuthenticationTokenFilter authenticationTokenFilter; | 75 | private JwtAuthenticationTokenFilter authenticationTokenFilter; |
| 76 | + /** | ||
| 77 | + * 自定义的权限映射 Bean | ||
| 78 | + * | ||
| 79 | + * @see #configure(HttpSecurity) | ||
| 80 | + */ | ||
| 81 | + @Resource | ||
| 82 | + private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer; | ||
| 77 | 83 | ||
| 78 | /** | 84 | /** |
| 79 | * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 | 85 | * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 |
| @@ -121,15 +127,16 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -121,15 +127,16 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 121 | .csrf().disable() | 127 | .csrf().disable() |
| 122 | // 基于 token 机制,所以不需要 Session | 128 | // 基于 token 机制,所以不需要 Session |
| 123 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() | 129 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() |
| 130 | + .headers().frameOptions().disable().and() | ||
| 124 | // 一堆自定义的 Spring Security 处理器 | 131 | // 一堆自定义的 Spring Security 处理器 |
| 125 | .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) | 132 | .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) |
| 126 | .accessDeniedHandler(accessDeniedHandler).and() | 133 | .accessDeniedHandler(accessDeniedHandler).and() |
| 127 | - // 设置每个请求的权限 | ||
| 128 | - .authorizeRequests() | 134 | + .logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出 |
| 135 | + | ||
| 136 | + // 设置每个请求的权限 ①:全局共享规则 | ||
| 137 | + httpSecurity.authorizeRequests() | ||
| 129 | // 登陆的接口,可匿名访问 | 138 | // 登陆的接口,可匿名访问 |
| 130 | .antMatchers(api("/login")).anonymous() | 139 | .antMatchers(api("/login")).anonymous() |
| 131 | - // 通用的接口,可匿名访问 TODO 芋艿:需要抽象出去 | ||
| 132 | - .antMatchers(api("/system/captcha/**")).anonymous() | ||
| 133 | // 静态资源,可匿名访问 | 140 | // 静态资源,可匿名访问 |
| 134 | .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() | 141 | .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() |
| 135 | // 文件的获取接口,可匿名访问 | 142 | // 文件的获取接口,可匿名访问 |
| @@ -139,22 +146,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -139,22 +146,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 139 | .antMatchers("/swagger-resources/**").anonymous() | 146 | .antMatchers("/swagger-resources/**").anonymous() |
| 140 | .antMatchers("/webjars/**").anonymous() | 147 | .antMatchers("/webjars/**").anonymous() |
| 141 | .antMatchers("/*/api-docs").anonymous() | 148 | .antMatchers("/*/api-docs").anonymous() |
| 142 | - // Spring Boot Admin Server 的安全配置 TODO 芋艿:需要抽象出去 | ||
| 143 | - .antMatchers(adminSeverContextPath).anonymous() | ||
| 144 | - .antMatchers(adminSeverContextPath + "/**").anonymous() | ||
| 145 | // Spring Boot Actuator 的安全配置 | 149 | // Spring Boot Actuator 的安全配置 |
| 146 | .antMatchers("/actuator").anonymous() | 150 | .antMatchers("/actuator").anonymous() |
| 147 | .antMatchers("/actuator/**").anonymous() | 151 | .antMatchers("/actuator/**").anonymous() |
| 148 | - // Druid 监控 TODO 芋艿:需要抽象出去 | 152 | + // Druid 监控 TODO 芋艿:等对接了 druid admin 后,在调整下。 |
| 149 | .antMatchers("/druid/**").anonymous() | 153 | .antMatchers("/druid/**").anonymous() |
| 150 | - // 短信回调 API TODO 芋艿:需要抽象出去 | ||
| 151 | - .antMatchers(api("/system/sms/callback/**")).anonymous() | ||
| 152 | - .antMatchers(api("/user/**")).anonymous() | ||
| 153 | - // 除上面外的所有请求全部需要鉴权认证 | ||
| 154 | - .anyRequest().authenticated() | ||
| 155 | - .and() | ||
| 156 | - .headers().frameOptions().disable(); | ||
| 157 | - httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); | 154 | + // 设置每个请求的权限 ②:每个项目的自定义规则 |
| 155 | + .and().authorizeRequests(authorizeRequestsCustomizer) | ||
| 156 | + // 设置每个请求的权限 ③:兜底规则,必须认证 | ||
| 157 | + .authorizeRequests().anyRequest().authenticated(); | ||
| 158 | // 添加 JWT Filter | 158 | // 添加 JWT Filter |
| 159 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); | 159 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
| 160 | } | 160 | } |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/annotations/PreAuthenticated.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.security.core.annotations; | ||
| 2 | + | ||
| 3 | +import java.lang.annotation.*; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 声明用户需要登陆 | ||
| 7 | + * | ||
| 8 | + * 为什么不使用 {@link org.springframework.security.access.prepost.PreAuthorize} 注解,原因是不通过时,抛出的是认证不通过,而不是未登陆 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Target({ElementType.METHOD}) | ||
| 13 | +@Retention(RetentionPolicy.RUNTIME) | ||
| 14 | +@Inherited | ||
| 15 | +@Documented | ||
| 16 | +public @interface PreAuthenticated { | ||
| 17 | +} |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/aop/PreAuthenticatedAspect.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.security.core.aop; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | ||
| 4 | +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 5 | +import lombok.extern.slf4j.Slf4j; | ||
| 6 | +import org.aspectj.lang.ProceedingJoinPoint; | ||
| 7 | +import org.aspectj.lang.annotation.Around; | ||
| 8 | +import org.aspectj.lang.annotation.Aspect; | ||
| 9 | + | ||
| 10 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; | ||
| 11 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 12 | + | ||
| 13 | +@Aspect | ||
| 14 | +@Slf4j | ||
| 15 | +public class PreAuthenticatedAspect { | ||
| 16 | + | ||
| 17 | + @Around("@annotation(preAuthenticated)") | ||
| 18 | + public Object around(ProceedingJoinPoint joinPoint, PreAuthenticated preAuthenticated) throws Throwable { | ||
| 19 | + if (SecurityFrameworkUtils.getLoginUser() == null) { | ||
| 20 | + throw exception(UNAUTHORIZED); | ||
| 21 | + } | ||
| 22 | + return joinPoint.proceed(); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/framework/security/SecurityConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.userserver.framework.security; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.web.config.WebProperties; | ||
| 4 | +import org.springframework.context.annotation.Bean; | ||
| 5 | +import org.springframework.context.annotation.Configuration; | ||
| 6 | +import org.springframework.security.config.Customizer; | ||
| 7 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
| 8 | +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; | ||
| 9 | + | ||
| 10 | +import javax.annotation.Resource; | ||
| 11 | + | ||
| 12 | +@Configuration | ||
| 13 | +public class SecurityConfiguration { | ||
| 14 | + | ||
| 15 | + @Resource | ||
| 16 | + private WebProperties webProperties; | ||
| 17 | + | ||
| 18 | + @Bean | ||
| 19 | + public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() { | ||
| 20 | + return registry -> { | ||
| 21 | + registry.antMatchers(api("/**")).anonymous(); // 默认 API 都是用户可访问 | ||
| 22 | + }; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + private String api(String url) { | ||
| 26 | + return webProperties.getApiPrefix() + url; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/infra/controller/HelloController.java
| 1 | package cn.iocoder.yudao.userserver.modules.infra.controller; | 1 | package cn.iocoder.yudao.userserver.modules.infra.controller; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | ||
| 3 | import lombok.extern.slf4j.Slf4j; | 4 | import lombok.extern.slf4j.Slf4j; |
| 5 | +import org.springframework.security.access.annotation.Secured; | ||
| 6 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 4 | import org.springframework.web.bind.annotation.RequestMapping; | 7 | import org.springframework.web.bind.annotation.RequestMapping; |
| 5 | import org.springframework.web.bind.annotation.RestController; | 8 | import org.springframework.web.bind.annotation.RestController; |
| 6 | 9 | ||
| @@ -15,4 +18,11 @@ public class HelloController { | @@ -15,4 +18,11 @@ public class HelloController { | ||
| 15 | public String hello(String hello) { | 18 | public String hello(String hello) { |
| 16 | return "echo + " + hello; | 19 | return "echo + " + hello; |
| 17 | } | 20 | } |
| 21 | + | ||
| 22 | + @RequestMapping("/user/info") | ||
| 23 | + @PreAuthenticated | ||
| 24 | + public String xx() { | ||
| 25 | + return "none"; | ||
| 26 | + } | ||
| 27 | + | ||
| 18 | } | 28 | } |