自定义权限校验,增加匿名访问注解,扩展PreAuthorize 匿名注解

pull/167/head
dqjdda 2019-10-30 11:51:03 +08:00
parent c9c86f046a
commit 938ae1fcd8
6 changed files with 63 additions and 19 deletions

View File

@ -0,0 +1,16 @@
package me.zhengjie.annotation;
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,6 +1,7 @@
package me.zhengjie.config; package me.zhengjie.config;
import me.zhengjie.utils.SecurityUtils; import me.zhengjie.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays; import java.util.Arrays;
@ -11,6 +12,11 @@ import java.util.stream.Collectors;
public class ElPermissionConfig { public class ElPermissionConfig {
public Boolean check(String ...permissions){ public Boolean check(String ...permissions){
// 如果是匿名访问的,就放行
String anonymous = "anonymous";
if(Arrays.asList(permissions).contains(anonymous)){
return true;
}
List<String> elPermissions = SecurityUtils.getUserDetails().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); List<String> elPermissions = SecurityUtils.getUserDetails().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
List<String> list = Arrays.stream(permissions).filter(elPermissions::contains).map(s -> s).collect(Collectors.toList()); List<String> list = Arrays.stream(permissions).filter(elPermissions::contains).map(s -> s).collect(Collectors.toList());
if(elPermissions.contains("admin") || list.size() != 0){ if(elPermissions.contains("admin") || list.size() != 0){

View File

@ -2,7 +2,9 @@ package me.zhengjie.modules.monitor.rest;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.annotation.Limit; import me.zhengjie.annotation.Limit;
import org.springframework.security.access.prepost.PreAuthorize;
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;
@ -17,12 +19,14 @@ import java.util.concurrent.atomic.AtomicInteger;
@RequestMapping("/api/limit") @RequestMapping("/api/limit")
@Api(tags = "系统:限流测试管理") @Api(tags = "系统:限流测试管理")
public class LimitController { public class LimitController {
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(); private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
/** /**
* 60访 10redis limit_test * 60访 10redis limit_test
*/ */
@GetMapping @GetMapping
@PreAuthorize("@el.check('anonymous')")
@ApiOperation("测试") @ApiOperation("测试")
@Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit") @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit")
public int testLimit() { public int testLimit() {

View File

@ -1,13 +1,17 @@
package me.zhengjie.modules.security.config; package me.zhengjie.modules.security.config;
import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.config.ElPermissionConfig;
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 +23,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
@ -29,16 +40,19 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtUserDetailsService jwtUserDetailsService; private final JwtUserDetailsService jwtUserDetailsService;
private final ApplicationContext applicationContext;
// 自定义基于JWT的安全过滤器 // 自定义基于JWT的安全过滤器
private final JwtAuthorizationTokenFilter authenticationTokenFilter; private final JwtAuthorizationTokenFilter authenticationTokenFilter;
@Value("${jwt.header}") @Value("${jwt.header}")
private String tokenHeader; private String tokenHeader;
public SecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, JwtUserDetailsService jwtUserDetailsService, JwtAuthorizationTokenFilter authenticationTokenFilter) { public SecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, JwtUserDetailsService jwtUserDetailsService, JwtAuthorizationTokenFilter authenticationTokenFilter, ApplicationContext applicationContext) {
this.unauthorizedHandler = unauthorizedHandler; this.unauthorizedHandler = unauthorizedHandler;
this.jwtUserDetailsService = jwtUserDetailsService; this.jwtUserDetailsService = jwtUserDetailsService;
this.authenticationTokenFilter = authenticationTokenFilter; this.authenticationTokenFilter = authenticationTokenFilter;
this.applicationContext = applicationContext;
} }
@Autowired @Autowired
@ -67,18 +81,26 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity httpSecurity) throws Exception { protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻 匿名标记 url PreAuthorize("hasAnyRole('anonymous')") 和 PreAuthorize("@el.check('anonymous')") 和 AnonymousAccess
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);
if (null != preAuthorize && preAuthorize.value().contains("anonymous")) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
} else if (null != anonymousAccess && null == preAuthorize) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
}
}
httpSecurity httpSecurity
// 禁用 CSRF // 禁用 CSRF
.csrf().disable() .csrf().disable()
// 授权异常 // 授权异常
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 不创建会话 // 不创建会话
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求 // 过滤请求
.authorizeRequests() .authorizeRequests()
.antMatchers( .antMatchers(
@ -88,31 +110,20 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
"/**/*.css", "/**/*.css",
"/**/*.js" "/**/*.js"
).anonymous() ).anonymous()
.antMatchers(HttpMethod.POST,"/auth/login").permitAll()
.antMatchers(HttpMethod.DELETE,"/auth/logout").permitAll()
.antMatchers(HttpMethod.GET,"/auth/code").permitAll()
// 支付宝回调
.antMatchers("/api/aliPay/return").permitAll()
.antMatchers("/api/aliPay/notify").permitAll()
// swagger start // swagger start
.antMatchers("/swagger-ui.html").permitAll() .antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll() .antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/webjars/**").permitAll() .antMatchers("/webjars/**").permitAll()
.antMatchers("/*/api-docs").permitAll() .antMatchers("/*/api-docs").permitAll()
// swagger end // swagger end
// 接口限流测试
.antMatchers("/test/**").permitAll()
// 文件 // 文件
.antMatchers("/avatar/**").permitAll() .antMatchers("/avatar/**").permitAll()
.antMatchers("/file/**").permitAll() .antMatchers("/file/**").permitAll()
// 放行OPTIONS请求 // 放行OPTIONS请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/druid/**").permitAll() .antMatchers("/druid/**").permitAll()
// 自定义匿名访问所有url放行 允许 匿名和带权限以及登录用户访问
.antMatchers(anonymousUrls.toArray(new String[0])).permitAll()
// 所有请求都需要认证 // 所有请求都需要认证
.anyRequest().authenticated() .anyRequest().authenticated()
// 防止iframe 造成跨域 // 防止iframe 造成跨域

View File

@ -5,6 +5,7 @@ import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.aop.log.Log; import me.zhengjie.aop.log.Log;
import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.monitor.service.RedisService; import me.zhengjie.modules.monitor.service.RedisService;
@ -58,6 +59,7 @@ public class AuthenticationController {
@Log("用户登录") @Log("用户登录")
@ApiOperation("登录授权") @ApiOperation("登录授权")
@AnonymousAccess
@PostMapping(value = "/login") @PostMapping(value = "/login")
public ResponseEntity login(@Validated @RequestBody AuthUser authorizationUser, HttpServletRequest request){ public ResponseEntity login(@Validated @RequestBody AuthUser authorizationUser, HttpServletRequest request){
@ -96,6 +98,7 @@ public class AuthenticationController {
} }
@ApiOperation("获取验证码") @ApiOperation("获取验证码")
@AnonymousAccess
@GetMapping(value = "/code") @GetMapping(value = "/code")
public ImgResult getCode(){ public ImgResult getCode(){
// 算术类型 https://gitee.com/whvse/EasyCaptcha // 算术类型 https://gitee.com/whvse/EasyCaptcha
@ -110,6 +113,7 @@ public class AuthenticationController {
} }
@ApiOperation("退出登录") @ApiOperation("退出登录")
@AnonymousAccess
@DeleteMapping(value = "/logout") @DeleteMapping(value = "/logout")
public ResponseEntity logout(HttpServletRequest request){ public ResponseEntity logout(HttpServletRequest request){
onlineUserService.logout(jwtTokenUtil.getToken(request)); onlineUserService.logout(jwtTokenUtil.getToken(request));

View File

@ -3,6 +3,7 @@ package me.zhengjie.rest;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.aop.log.Log; import me.zhengjie.aop.log.Log;
import me.zhengjie.domain.AlipayConfig; import me.zhengjie.domain.AlipayConfig;
import me.zhengjie.domain.vo.TradeVo; import me.zhengjie.domain.vo.TradeVo;
@ -74,6 +75,7 @@ public class AliPayController {
@ApiIgnore @ApiIgnore
@GetMapping("/return") @GetMapping("/return")
@AnonymousAccess
@ApiOperation("支付之后跳转的链接") @ApiOperation("支付之后跳转的链接")
public ResponseEntity<String> returnPage(HttpServletRequest request, HttpServletResponse response){ public ResponseEntity<String> returnPage(HttpServletRequest request, HttpServletResponse response){
AlipayConfig alipay = alipayService.find(); AlipayConfig alipay = alipayService.find();
@ -96,6 +98,7 @@ public class AliPayController {
@ApiIgnore @ApiIgnore
@RequestMapping("/notify") @RequestMapping("/notify")
@AnonymousAccess
@ApiOperation("支付异步通知(要公网访问)接收异步通知检查通知内容app_id、out_trade_no、total_amount是否与请求中的一致根据trade_status进行后续业务处理") @ApiOperation("支付异步通知(要公网访问)接收异步通知检查通知内容app_id、out_trade_no、total_amount是否与请求中的一致根据trade_status进行后续业务处理")
public ResponseEntity notify(HttpServletRequest request){ public ResponseEntity notify(HttpServletRequest request){
AlipayConfig alipay = alipayService.find(); AlipayConfig alipay = alipayService.find();