Commit 689171c18dca9304df6edd9efd1a90a1a9b35a1e

Authored by YunaiV
1 parent 0439a550

增加 @PreAuthenticated 注解,实现登陆的拦截

@@ -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 }