作者 RuoYi

新增Anonymous匿名访问不鉴权注解

  1 +package com.ruoyi.common.annotation;
  2 +
  3 +import java.lang.annotation.Documented;
  4 +import java.lang.annotation.ElementType;
  5 +import java.lang.annotation.Retention;
  6 +import java.lang.annotation.RetentionPolicy;
  7 +import java.lang.annotation.Target;
  8 +
  9 +/**
  10 + * 匿名访问不鉴权注解
  11 + *
  12 + * @author ruoyi
  13 + */
  14 +@Target({ ElementType.METHOD, ElementType.TYPE })
  15 +@Retention(RetentionPolicy.RUNTIME)
  16 +@Documented
  17 +public @interface Anonymous
  18 +{
  19 +}
@@ -8,12 +8,14 @@ import org.springframework.security.config.annotation.authentication.builders.Au @@ -8,12 +8,14 @@ import org.springframework.security.config.annotation.authentication.builders.Au
8 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 8 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
9 import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  11 +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
11 import org.springframework.security.config.http.SessionCreationPolicy; 12 import org.springframework.security.config.http.SessionCreationPolicy;
12 import org.springframework.security.core.userdetails.UserDetailsService; 13 import org.springframework.security.core.userdetails.UserDetailsService;
13 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 15 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
15 import org.springframework.security.web.authentication.logout.LogoutFilter; 16 import org.springframework.security.web.authentication.logout.LogoutFilter;
16 import org.springframework.web.filter.CorsFilter; 17 import org.springframework.web.filter.CorsFilter;
  18 +import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
17 import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; 19 import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
18 import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; 20 import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
19 import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; 21 import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
@@ -55,7 +57,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter @@ -55,7 +57,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
55 */ 57 */
56 @Autowired 58 @Autowired
57 private CorsFilter corsFilter; 59 private CorsFilter corsFilter;
58 - 60 +
  61 + /**
  62 + * 允许匿名访问的地址
  63 + */
  64 + @Autowired
  65 + private PermitAllUrlProperties permitAllUrl;
  66 +
59 /** 67 /**
60 * 解决 无法直接注入 AuthenticationManager 68 * 解决 无法直接注入 AuthenticationManager
61 * 69 *
@@ -87,6 +95,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter @@ -87,6 +95,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
87 @Override 95 @Override
88 protected void configure(HttpSecurity httpSecurity) throws Exception 96 protected void configure(HttpSecurity httpSecurity) throws Exception
89 { 97 {
  98 + // 注解标记允许匿名访问的url
  99 + ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
  100 + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
  101 +
90 httpSecurity 102 httpSecurity
91 // CSRF禁用,因为不使用session 103 // CSRF禁用,因为不使用session
92 .csrf().disable() 104 .csrf().disable()
@@ -98,24 +110,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter @@ -98,24 +110,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
98 .authorizeRequests() 110 .authorizeRequests()
99 // 对于登录login 注册register 验证码captchaImage 允许匿名访问 111 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
100 .antMatchers("/login", "/register", "/captchaImage").anonymous() 112 .antMatchers("/login", "/register", "/captchaImage").anonymous()
101 - .antMatchers(  
102 - HttpMethod.GET,  
103 - "/",  
104 - "/*.html",  
105 - "/**/*.html",  
106 - "/**/*.css",  
107 - "/**/*.js",  
108 - "/profile/**"  
109 - ).permitAll()  
110 - .antMatchers("/swagger-ui.html").anonymous()  
111 - .antMatchers("/swagger-resources/**").anonymous()  
112 - .antMatchers("/webjars/**").anonymous()  
113 - .antMatchers("/*/api-docs").anonymous()  
114 - .antMatchers("/druid/**").anonymous() 113 + // 静态资源,可匿名访问
  114 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
  115 + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
115 // 除上面外的所有请求全部需要鉴权认证 116 // 除上面外的所有请求全部需要鉴权认证
116 .anyRequest().authenticated() 117 .anyRequest().authenticated()
117 .and() 118 .and()
118 .headers().frameOptions().disable(); 119 .headers().frameOptions().disable();
  120 + // 添加Logout filter
119 httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); 121 httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
120 // 添加JWT filter 122 // 添加JWT filter
121 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 123 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  1 +package com.ruoyi.framework.config.properties;
  2 +
  3 +import java.util.ArrayList;
  4 +import java.util.List;
  5 +import java.util.Map;
  6 +import java.util.Optional;
  7 +import java.util.regex.Pattern;
  8 +import org.apache.commons.lang3.RegExUtils;
  9 +import org.springframework.beans.BeansException;
  10 +import org.springframework.beans.factory.InitializingBean;
  11 +import org.springframework.context.ApplicationContext;
  12 +import org.springframework.context.ApplicationContextAware;
  13 +import org.springframework.context.annotation.Configuration;
  14 +import org.springframework.core.annotation.AnnotationUtils;
  15 +import org.springframework.web.method.HandlerMethod;
  16 +import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
  17 +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
  18 +import com.ruoyi.common.annotation.Anonymous;
  19 +
  20 +/**
  21 + * 设置Anonymous注解允许匿名访问的url
  22 + *
  23 + * @author ruoyi
  24 + */
  25 +@Configuration
  26 +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware
  27 +{
  28 + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
  29 +
  30 + private ApplicationContext applicationContext;
  31 +
  32 + private List<String> urls = new ArrayList<>();
  33 +
  34 + public String ASTERISK = "*";
  35 +
  36 + @Override
  37 + public void afterPropertiesSet()
  38 + {
  39 + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
  40 + Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
  41 +
  42 + map.keySet().forEach(info -> {
  43 + HandlerMethod handlerMethod = map.get(info);
  44 +
  45 + // 获取方法上边的注解 替代path variable 为 *
  46 + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
  47 + Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
  48 + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
  49 +
  50 + // 获取类上边的注解, 替代path variable 为 *
  51 + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
  52 + Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
  53 + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
  54 + });
  55 + }
  56 +
  57 + @Override
  58 + public void setApplicationContext(ApplicationContext context) throws BeansException
  59 + {
  60 + this.applicationContext = context;
  61 + }
  62 +
  63 + public List<String> getUrls()
  64 + {
  65 + return urls;
  66 + }
  67 +
  68 + public void setUrls(List<String> urls)
  69 + {
  70 + this.urls = urls;
  71 + }
  72 +}