diff --git a/eladmin-system/pom.xml b/eladmin-system/pom.xml index fbc1e007..09b9bcfc 100644 --- a/eladmin-system/pom.xml +++ b/eladmin-system/pom.xml @@ -35,6 +35,12 @@ 2.2 + + org.springframework.boot + spring-boot-devtools + true + + io.jsonwebtoken diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/AnonymousAccessController.java b/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/AnonymousAccessController.java new file mode 100644 index 00000000..1b304dc8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/AnonymousAccessController.java @@ -0,0 +1,38 @@ +package me.zhengjie.modules.monitor.rest; + +import me.zhengjie.modules.security.annotation.AnonymousAccess; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 匿名访问测试类 + */ +@RestController +@RequestMapping("api") +public class AnonymousAccessController { + private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(); + + + @GetMapping("/anonymousAccess1") + @AnonymousAccess + public int testAnonymousAccess1() { + return ATOMIC_INTEGER.incrementAndGet(); + } + + @GetMapping("/anonymousAccess2") + @AnonymousAccess + @PreAuthorize("hasAnyRole('ROLE_ANONYMOUS')") + public int testAnonymousAccess2() { + return ATOMIC_INTEGER.incrementAndGet(); + } + + @GetMapping("/anonymousAccess3") + @PreAuthorize("hasAnyRole('ROLE_ANONYMOUS')") + public int testAnonymousAccess3() { + return ATOMIC_INTEGER.incrementAndGet(); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/LimitController.java b/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/LimitController.java index 3a742cac..1802d3a5 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/LimitController.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/monitor/rest/LimitController.java @@ -1,6 +1,7 @@ package me.zhengjie.modules.monitor.rest; import me.zhengjie.annotation.Limit; +import me.zhengjie.modules.security.annotation.AnonymousAccess; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -21,6 +22,7 @@ public class LimitController { */ @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit") @GetMapping("/limit") + @AnonymousAccess public int testLimit() { return ATOMIC_INTEGER.incrementAndGet(); } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/annotation/AnonymousAccess.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/annotation/AnonymousAccess.java new file mode 100644 index 00000000..661792cf --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/annotation/AnonymousAccess.java @@ -0,0 +1,18 @@ +package me.zhengjie.modules.security.annotation; + +import me.zhengjie.aspect.LimitType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author jacky + * 用于标记匿名访问方法 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AnonymousAccess { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java index d10bd218..4aa256cb 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java @@ -1,13 +1,16 @@ package me.zhengjie.modules.security.config; +import me.zhengjie.modules.security.annotation.AnonymousAccess; import me.zhengjie.modules.security.security.JwtAuthenticationEntryPoint; import me.zhengjie.modules.security.security.JwtAuthorizationTokenFilter; import me.zhengjie.modules.security.service.JwtUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -19,6 +22,13 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +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.HashSet; +import java.util.Map; +import java.util.Set; @Configuration @EnableWebSecurity @@ -30,6 +40,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtUserDetailsService jwtUserDetailsService; + @Autowired + private ApplicationContext applicationContext; /** * 自定义基于JWT的安全过滤器 @@ -50,6 +62,17 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .passwordEncoder(passwordEncoderBean()); } +// @Bean +// public AnonymousAuthenticationFilter anonymousAuthenticationFilter() { +// AnonymousAuthenticationFilter authenticationFilter = new AnonymousAuthenticationFilter("anonymous"); +// return authenticationFilter; +// } +// +// @Bean +// public AnonymousAuthenticationProvider anonymousAuthenticationProvider() { +// return new AnonymousAuthenticationProvider("anonymous"); +// } + @Bean GrantedAuthorityDefaults grantedAuthorityDefaults() { // Remove the ROLE_ prefix @@ -69,7 +92,19 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception { - + Map handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods(); + Set anonymousUrls = new HashSet<>(); + for (Map.Entry infoEntry : handlerMethodMap.entrySet()) { + HandlerMethod handlerMethod = infoEntry.getValue(); + AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class); + PreAuthorize preAuthorize = handlerMethod.getMethodAnnotation(PreAuthorize.class); + // PreAuthorize("hasAnyRole('ROLE_ANONYMOUS')") 和 AnonymousAccess + if (null != preAuthorize && preAuthorize.value().contains("ROLE_ANONYMOUS")) { + anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + } else if (null != anonymousAccess && null == preAuthorize) { + anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + } + } httpSecurity // 禁用 CSRF @@ -91,7 +126,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { "/**/*.js" ).anonymous() - .antMatchers( HttpMethod.POST,"/auth/"+loginPath).anonymous() + .antMatchers(HttpMethod.POST, "/auth/" + loginPath).anonymous() .antMatchers("/auth/vCode").anonymous() // 支付宝回调 .antMatchers("/api/aliPay/return").anonymous() @@ -114,12 +149,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .antMatchers(HttpMethod.OPTIONS, "/**").anonymous() .antMatchers("/druid/**").anonymous() + // 自定义匿名访问所有url放行 + .antMatchers(anonymousUrls.toArray(new String[0])).anonymous() // 所有请求都需要认证 .anyRequest().authenticated() + // 防止iframe 造成跨域 .and().headers().frameOptions().disable(); - httpSecurity - .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java index f1504671..9dfbb938 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java @@ -21,6 +21,6 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Se /** * 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 */ - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException == null ? "Unauthorized" : authException.getMessage()); } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java index 1693bcc1..8ca24fdf 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java @@ -5,7 +5,9 @@ import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.security.utils.JwtTokenUtil; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -61,6 +63,10 @@ public class JwtAuthorizationTokenFilter extends OncePerRequestFilter { authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } + } else { +// AnonymousAuthenticationToken anonymousAuthenticationToken = new AnonymousAuthenticationToken("anonymous", "anonymousUser", AuthorityUtils.createAuthorityList(new String[]{"ROLE_ANONYMOUS"})); +// anonymousAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); +// SecurityContextHolder.getContext().setAuthentication(anonymousAuthenticationToken); } chain.doFilter(request, response); }