提交 746749e5 编写于 作者: M ManongJu

动态权限校验

上级 65e938f3
......@@ -24,11 +24,6 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
......
package com.microservice.skeleton.common.jwt;
/**
* @author Mr.Yangxiufeng
* @date 2020-10-29
* @time 9:11
*/
public class JWTConstants {
public static final byte[] SECRET = "52d907a4b404af790cf2cf488acc4836".getBytes();
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
}
package com.microservice.skeleton.common.util;
import java.io.UnsupportedEncodingException;
/**
* @author Mr.Yangxiufeng
* @date 2020-10-29
* @time 9:17
*/
public class Md5Utils {
private static final int HEX_VALUE_COUNT = 16;
public static String getMD5(byte[] bytes) {
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
char[] str = new char[16 * 2];
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
md.update(bytes);
byte[] tmp = md.digest();
int k = 0;
for (int i = 0; i < HEX_VALUE_COUNT; i++) {
byte byte0 = tmp[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
} catch (Exception e) {
e.printStackTrace();
}
return new String(str);
}
public static String getMD5(String value, String encode) {
String result = "";
try {
result = getMD5(value.getBytes(encode));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
}
package com.microservice.skeleton.common.util;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Mr.Yangxiufeng
* Date: 2018-12-07
* Time: 14:02
*/
public class StatusCode {
public static final int SUCCESS_CODE = 200;
public static final String SUCCESS_MSG = "操作成功";
......
package com.microservice.skeleton.common.vo;
/**
* @author Mr.Yangxiufeng
* @date 2020-10-29
* @time 14:38
*/
public class Authority {
private String authority;
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
package com.microservice.skeleton.common.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.Date;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MenuVo {
private Integer id;
private String code;
......
package com.microservice.skeleton.common.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Mr.Yangxiufeng
* Date: 2018-05-16
* Time: 11:04
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class R {
private Integer code;
private String msg;
private String description;
private Object data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public R() {
}
private R(Builder builder) {
this.code = builder.code;
this.msg = builder.msg;
this.description = builder.description;
this.data = builder.data;
}
@Data
public static class Builder{
private Integer code;
private String msg;
private String description;
private Object data;
public Builder code(Integer code) {
this.code = code;
return this;
}
public Builder msg(String msg) {
this.msg = msg;
return this;
}
public Builder description(String description) {
this.description = description;
return this;
}
public Builder data(Object data) {
this.data = data;
return this;
}
public R build(){
return new R(this);
}
}
}
package com.microservice.skeleton.common.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.HashMap;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Result<T> {
private static final String CODE = "code";
......
package com.microservice.skeleton.common.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
......@@ -8,7 +7,6 @@ import java.util.Date;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RoleVo implements Serializable {
private static final long serialVersionUID = 2179037393108205286L;
private Integer id;
......
package com.microservice.skeleton.common.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
......@@ -8,7 +7,6 @@ import java.util.Date;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserVo implements Serializable {
private static final long serialVersionUID = 3881610071550902762L;
......
......@@ -26,16 +26,21 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.microservice.skeleton.common</groupId>
<artifactId>mss-common</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth.version}</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
......@@ -46,12 +51,13 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.1.1</version>
</dependency>
</dependencies>
<build>
......
......@@ -2,14 +2,10 @@ package com.microservice.skeleton.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
......
package com.microservice.skeleton.gateway.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Mr.Yangxiufeng
* @date 2020-10-29
* @time 10:21
*/
@Component
@ConfigurationProperties(prefix = "exclusion")
public class ExclusionUrl {
private List<String> url;
public List<String> getUrl() {
return url;
}
public void setUrl(List<String> url) {
this.url = url;
}
}
package com.microservice.skeleton.gateway.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
/**
* Created by Mr.Yangxiufeng on 2017/12/29.
* Time:10:08
* ProjectName:Mirco-Service-Skeleton
*/
@Configuration
@EnableResourceServer
public class SecurityConfig extends ResourceServerConfigurerAdapter {
@Autowired
private OAuth2WebSecurityExpressionHandler expressionHandler;
private static final String[] AUTH_WHITELIST = {
"/**/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"swagger-resources/configuration/ui",
"/doc.html",
"/webjars/**"
};
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/v2/api-docs","/uaa/**").permitAll();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
.authorizeRequests();
for (String au:AUTH_WHITELIST
) {
http.authorizeRequests().antMatchers(au).permitAll();
}
http.authorizeRequests().anyRequest().authenticated();
registry.anyRequest()
.access("@permissionService.hasPermission(request,authentication)");
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.expressionHandler(expressionHandler);
}
@Bean
public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
expressionHandler.setApplicationContext(applicationContext);
return expressionHandler;
}
}
package com.microservice.skeleton.gateway.config.swagger;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Mr.Yangxiufeng
* Date: 2018-04-18
* Time: 14:56
*/
@Component
@Primary
public class GatewaySwaggerResourcesProvider implements SwaggerResourcesProvider {
private final RouteLocator routeLocator;
public GatewaySwaggerResourcesProvider(RouteLocator routeLocator) {
this.routeLocator = routeLocator;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<Route> routes = routeLocator.getRoutes();
routes.forEach(route -> resources.add(swaggerResource(route.getId(), route.getFullPath().replace("**", "v2/api-docs"))));
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
package com.microservice.skeleton.gateway.config.swagger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Mr.Yangxiufeng
* Date: 2018-04-18
* Time: 14:55
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("说明文档")
.description("接口说明文档")
.termsOfServiceUrl("")
.contact(new Contact("杨秀峰","franky.yang@foxmail.com","franky.yang@foxmail.com"))
.version("1.0")
.build();
}
}
package com.microservice.skeleton.gateway.fallback;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microservice.skeleton.gateway.model.ErrorCode;
import com.microservice.skeleton.gateway.model.Msg;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by Mr.Yangxiufeng on 2017/12/26.
* Time:10:12
* ProjectName:Mirco-Service-Skeleton
*/
@Component
public class ServiceFallbackProvider implements FallbackProvider {
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
cause.printStackTrace();
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return getStatusCode().value();
}
@Override
public String getStatusText() throws IOException {
return getStatusCode().getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
//响应体
Msg msg = new Msg();
msg.setCode(ErrorCode.MICRO_SERVICE_UNAVAILABLE);
msg.setMsg("微服务不可用,请稍后再试");
ObjectMapper objectMapper = new ObjectMapper();
String content = objectMapper.writeValueAsString(msg);
return new ByteArrayInputStream(content.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
return httpHeaders;
}
};
}
@Override
public String getRoute() {
//表明是为哪个微服务提供回退,"*"全部
return "*";
}
}
package com.microservice.skeleton.gateway.filter;
import com.alibaba.fastjson.JSON;
import com.microservice.skeleton.common.jwt.JWTConstants;
import com.microservice.skeleton.common.util.Md5Utils;
import com.microservice.skeleton.common.vo.Authority;
import com.microservice.skeleton.common.vo.Result;
import com.microservice.skeleton.common.vo.UserVo;
import com.microservice.skeleton.gateway.config.ExclusionUrl;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.SignedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;