增加匿名访问注解,扩展PreAuthorize 匿名注解

测试用例: AnonymousAccessController.java
使用说明: 1. 在需要匿名访问的控制器方法上增加  @AnonymousAccess注解
          2. 在需要匿名访问的控制器方法添加 @PreAuthorize("hasAnyRole('ROLE_ANONYMOUS')")
pull/135/head
Your Name 2019-09-29 21:38:00 +08:00
parent 85a8bb2e54
commit 8a8eb9d0a6
7 changed files with 112 additions and 5 deletions

View File

@ -35,6 +35,12 @@
<version>2.2</version> <version>2.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--jwt--> <!--jwt-->
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>

View File

@ -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();
}
}

View File

@ -1,6 +1,7 @@
package me.zhengjie.modules.monitor.rest; package me.zhengjie.modules.monitor.rest;
import me.zhengjie.annotation.Limit; 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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; 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") @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit")
@GetMapping("/limit") @GetMapping("/limit")
@AnonymousAccess
public int testLimit() { public int testLimit() {
return ATOMIC_INTEGER.incrementAndGet(); return ATOMIC_INTEGER.incrementAndGet();
} }

View File

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

View File

@ -1,13 +1,16 @@
package me.zhengjie.modules.security.config; 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.JwtAuthenticationEntryPoint;
import me.zhengjie.modules.security.security.JwtAuthorizationTokenFilter; import me.zhengjie.modules.security.security.JwtAuthorizationTokenFilter;
import me.zhengjie.modules.security.service.JwtUserDetailsService; import me.zhengjie.modules.security.service.JwtUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 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 @Configuration
@EnableWebSecurity @EnableWebSecurity
@ -30,6 +40,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired @Autowired
private JwtUserDetailsService jwtUserDetailsService; private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private ApplicationContext applicationContext;
/** /**
* JWT * JWT
@ -50,6 +62,17 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.passwordEncoder(passwordEncoderBean()); .passwordEncoder(passwordEncoderBean());
} }
// @Bean
// public AnonymousAuthenticationFilter anonymousAuthenticationFilter() {
// AnonymousAuthenticationFilter authenticationFilter = new AnonymousAuthenticationFilter("anonymous");
// return authenticationFilter;
// }
//
// @Bean
// public AnonymousAuthenticationProvider anonymousAuthenticationProvider() {
// return new AnonymousAuthenticationProvider("anonymous");
// }
@Bean @Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() { GrantedAuthorityDefaults grantedAuthorityDefaults() {
// Remove the ROLE_ prefix // Remove the ROLE_ prefix
@ -69,7 +92,19 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity httpSecurity) throws Exception { protected void configure(HttpSecurity httpSecurity) throws Exception {
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Set<String> anonymousUrls = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> 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 httpSecurity
// 禁用 CSRF // 禁用 CSRF
@ -91,7 +126,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
"/**/*.js" "/**/*.js"
).anonymous() ).anonymous()
.antMatchers( HttpMethod.POST,"/auth/"+loginPath).anonymous() .antMatchers(HttpMethod.POST, "/auth/" + loginPath).anonymous()
.antMatchers("/auth/vCode").anonymous() .antMatchers("/auth/vCode").anonymous()
// 支付宝回调 // 支付宝回调
.antMatchers("/api/aliPay/return").anonymous() .antMatchers("/api/aliPay/return").anonymous()
@ -114,12 +149,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers(HttpMethod.OPTIONS, "/**").anonymous() .antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
.antMatchers("/druid/**").anonymous() .antMatchers("/druid/**").anonymous()
// 自定义匿名访问所有url放行
.antMatchers(anonymousUrls.toArray(new String[0])).anonymous()
// 所有请求都需要认证 // 所有请求都需要认证
.anyRequest().authenticated() .anyRequest().authenticated()
// 防止iframe 造成跨域 // 防止iframe 造成跨域
.and().headers().frameOptions().disable(); .and().headers().frameOptions().disable();
httpSecurity httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
} }
} }

View File

@ -21,6 +21,6 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Se
/** /**
* 访REST401 * 访REST401
*/ */
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException == null ? "Unauthorized" : authException.getMessage());
} }
} }

View File

@ -5,7 +5,9 @@ import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.security.utils.JwtTokenUtil; import me.zhengjie.modules.security.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 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.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
@ -61,6 +63,10 @@ public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication); 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); chain.doFilter(request, response);
} }