提交 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;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
/**
* @author Mr.Yangxiufeng
* @date 2020-10-29
* @time 9:29
*/
@Component
@Slf4j
public class AuthFilter implements GlobalFilter , Ordered {
@Autowired
private ExclusionUrl exclusionUrl;
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String headerToken = request.getHeaders().getFirst(JWTConstants.TOKEN_HEADER);
log.info("headerToken:{}", headerToken);
//1、只要带上了token, 就需要判断Token是否有效
if ( !StringUtils.isEmpty(headerToken) && !verifierToken(headerToken)){
return getVoidMono(response, 401, "token无效");
}
String path = request.getURI().getPath();
log.info("request path:{}", path);
//2、判断是否是过滤的路径, 是的话就放行
if ( isExclusionUrl(path) ){
return chain.filter(exchange);
}
//3、判断请求的URL是否有权限
boolean permission = hasPermission(headerToken , path);
if (!permission){
return getVoidMono(response, 403, "无访问权限");
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
private Mono<Void> getVoidMono(ServerHttpResponse response, int i, String msg) {
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
response.setStatusCode(HttpStatus.OK);
Result failed = Result.failure(i, msg);
byte[] bits = JSON.toJSONString(failed).getBytes();
DataBuffer buffer = response.bufferFactory().wrap(bits);
return response.writeWith(Mono.just(buffer));
}
private boolean isExclusionUrl(String path){
final String url = getRealUrl(path);
List<String> exclusions = exclusionUrl.getUrl();
if (exclusions.size() == 0){
return false;
}
return exclusions.stream().anyMatch( action -> antPathMatcher.match(action , url));
}
private boolean verifierToken(String headerToken){
try {
SignedJWT jwt = getSignedJWT(headerToken);
JWSVerifier verifier = new MACVerifier(JWTConstants.SECRET);
//校验是否有效
if (!jwt.verify(verifier)) {
log.error("token不合法,检测不过关");
return false;
}
//校验超时
Date expirationTime = jwt.getJWTClaimsSet().getExpirationTime();
if (new Date().after(expirationTime)) {
log.error("token已经过期");
return false;
}
//获取载体中的数据
return true;
} catch (ParseException | JOSEException e) {
log.error("token校验出错",e);
}
return false;
}
private boolean hasPermission(String headerToken, String path){
String url = getRealUrl(path);
try {
SignedJWT jwt = getSignedJWT(headerToken);
Object payload = jwt.getJWTClaimsSet().getClaim("payload");
UserVo user = JSON.parseObject(payload.toString(), UserVo.class);
//生成Key, 把权限放入到redis中
String keyPrefix = "JWT" + user.getId() + ":";
String token = headerToken.replace(JWTConstants.TOKEN_PREFIX, "");
String keySuffix = Md5Utils.getMD5(token.getBytes());
String key = keyPrefix + keySuffix;
String authKey = key + ":Authorities";
String authStr = redisTemplate.opsForValue().get(authKey);
if (StringUtils.isEmpty(authStr)){
return false;
}
List<Authority> authorities = JSON.parseArray(authStr , Authority.class);
return authorities.stream().anyMatch(authority -> antPathMatcher.match(authority.getAuthority(), url));
} catch (ParseException e) {
e.printStackTrace();
}
return false;
}
private String getRealUrl(String path){
String[] splitUrl = path.split("/");
return path.replace("/"+splitUrl[1] , "");
}
private SignedJWT getSignedJWT(String headerToken) throws ParseException {
String token = headerToken.replace(JWTConstants.TOKEN_PREFIX, "");
log.info("token is {}", token);
return SignedJWT.parse(token);
}
}
package com.microservice.skeleton.gateway.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* Created by Mr.Yangxiufeng on 2017/12/25.
* Time:15:39
* ProjectName:Mirco-Service-Skeleton
*/
@Component
public class PreRequestFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(PreRequestFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;//int值来定义过滤器的执行顺序,数值越小优先级越高
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LOG.info("send {} request to {}",request.getMethod(),request.getRequestURL().toString());
return null;
}
}
package com.microservice.skeleton.gateway.model;
/**
* Created by Mr.Yangxiufeng on 2017/12/26.
* Time:13:50
* ProjectName:Mirco-Service-Skeleton
*/
public class ErrorCode {
public static final int OK=200;
public static final int BAD_REQUEST=400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUNT = 404;
public static final int MICRO_SERVICE_UNAVAILABLE = 40001;//微服务不可用
public static final int SERVER_ERROR=500;
public static final int BAD_GATEWAY = 502;
public static final int SERVICE_UNAVAILABLE=503;
}
package com.microservice.skeleton.gateway.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
/**
* Created by Mr.Yangxiufeng on 2017/12/26.
* Time:13:41
* ProjectName:Mirco-Service-Skeleton
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Msg<T> implements Serializable{
private static final long serialVersionUID = -1177183613782210351L;
private Integer code;
private String msg;
private T 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 T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Msg{" +
"code=" + code +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
package com.microservice.skeleton.gateway.service;
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Mr.Yangxiufeng
* Date: 2018-05-14
* Time: 16:01
*/
public interface PermissionService {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
package com.microservice.skeleton.gateway.service.impl;
import com.microservice.skeleton.gateway.service.PermissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Mr.Yangxiufeng
* Date: 2018-05-14
* Time: 16:01
*/
@Service("permissionService")
@Slf4j
public class PermissionServiceImpl implements PermissionService {
/**
* 可以做URLs匹配,规则如下
*
* ?匹配一个字符
* *匹配0个或多个字符
* **匹配0个或多个目录
* 用例如下
* <p>https://www.cnblogs.com/zhangxiaoguang/p/5855113.html</p>
*/
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
String requestUrl = request.getRequestURI();
log.info("requestUrl:{}",requestUrl);
List<SimpleGrantedAuthority> grantedAuthorityList = (List<SimpleGrantedAuthority>) authentication.getAuthorities();
boolean hasPermission = false;
if (principal != null){
if (CollectionUtils.isEmpty(grantedAuthorityList)){
return hasPermission;
}
for (SimpleGrantedAuthority authority:grantedAuthorityList
) {
if (antPathMatcher.match(authority.getAuthority(),requestUrl)){
hasPermission = true;
break;
}
}
}
return hasPermission;
}
}
package com.microservice.skeleton.gateway.util;
import java.io.*;
/**
* Created by Mr.Yangxiufeng on 2017/12/26.
* Time:14:03
* ProjectName:Mirco-Service-Skeleton
*/
public class ObjectByteUtil {
public static byte[] toByteArray(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
}
/**
* 数组转对象
*
* @param bytes
* @return
*/
public static Object toObject(byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
}
......@@ -6,19 +6,19 @@ spring:
loadbalancer:
retry:
enabled: true
zuul:
host:
connect-timeout-millis: 10000
socket-timeout-millis: 60000
routes:
uaa:
sensitiveHeaders:
security:
oauth2:
resource:
user-info-uri: http://uaa/user
prefer-token-info: false
loadBalanced: true
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
locale: zh_CN
time-zone: GMT+8
redis:
host: 127.0.0.1
port: 6379
password: 123456
##############end#####################
####超时配置####
ribbon:
......@@ -26,8 +26,6 @@ ribbon:
ConnectTimeout: 10000
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
eureka:
enabled: true
hystrix:
command:
default:
......@@ -37,4 +35,7 @@ hystrix:
isolation:
thread:
timeoutInMilliseconds: 600000
###超时配置###
\ No newline at end of file
exclusion:
url:
- /goods/list
- /goods/detail
\ No newline at end of file
package com.microservice.skeleton.auth.controller;
import com.microservice.skeleton.common.vo.Result;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LogoutController {
@DeleteMapping(value = "/exit")
public @ResponseBody
Result revokeToken(String access_token){
return Result.ok();
}
}
......@@ -7,7 +7,7 @@ import java.security.Principal;
@RestController
public class UserController {
@RequestMapping("/user")
@RequestMapping("/checkUser")
public Principal user(Principal user) {
return user;
}
......
......@@ -14,7 +14,7 @@ import java.util.Set;
*/
public class AuthUser implements UserDetails {
private Integer userId;
private Integer id;
private String password;
private final String username;
private Set<SimpleGrantedAuthority> authorities;
......@@ -72,11 +72,11 @@ public class AuthUser implements UserDetails {
return enabled;
}
public Integer getUserId() {
return userId;
public Integer getId() {
return id;
}
public void setUserId(Integer userId) {
this.userId = userId;
public void setId(Integer id) {
this.id = id;
}
}
package com.microservice.skeleton.auth.jwt;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.common.utils.Md5Utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microservice.skeleton.auth.entity.AuthUser;
import com.microservice.skeleton.common.jwt.JWTConstants;
import com.microservice.skeleton.common.util.Md5Utils;
import com.microservice.skeleton.common.vo.Result;
import com.microservice.skeleton.common.vo.UserVo;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.Md5Crypt;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
......@@ -22,10 +20,8 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import sun.security.provider.MD5;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
......@@ -45,8 +41,6 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final byte[] SECRET = "6MNSobBRCHGIO0fS6MNSobBRCHGIO0fS".getBytes();
/**
* 过期时间2小时
*/
......@@ -72,15 +66,13 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
AuthUser user = (AuthUser) authResult.getPrincipal();
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
/**
* 1、创建密钥
*/
MACSigner macSigner = new MACSigner(SECRET);
MACSigner macSigner = new MACSigner(JWTConstants.SECRET);
/**
* 2、payload
*/
String payload = JSONObject.toJSONString(user);
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("subject")
......@@ -100,12 +92,16 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
Result result = Result.ok().setData(jwtToken);
//
String keyPrefix = "JWT" + user.getUserId() + ":";
//生成Key, 把权限放入到redis中
String keyPrefix = "JWT" + user.getId() + ":";
String keySuffix = Md5Utils.getMD5(jwtToken.getBytes());
String key = keyPrefix + keySuffix;
redisTemplate.opsForValue().set(key , jwtToken , EXPIRE_TIME , TimeUnit.SECONDS);
String authKey = key + ":Authorities";
redisTemplate.opsForValue().set(key , jwtToken , EXPIRE_TIME , TimeUnit.MILLISECONDS);
redisTemplate.opsForValue().set(authKey, JSONObject.toJSONString(user.getAuthorities()), EXPIRE_TIME , TimeUnit.SECONDS);
response.getWriter().write(JSONObject.toJSONString(result));
}
......
package com.microservice.skeleton.auth.jwt;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.Md5Utils;
import com.microservice.skeleton.auth.entity.AuthUser;
import com.microservice.skeleton.common.jwt.JWTConstants;
import com.microservice.skeleton.common.vo.Result;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSVerifier;
......@@ -16,8 +16,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
......@@ -28,7 +26,6 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**<p>授权</p>
* @author Mr.Yangxiufeng
......@@ -38,20 +35,14 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private static final byte[] SECRET = "6MNSobBRCHGIO0fS6MNSobBRCHGIO0fS".getBytes();
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = request.getHeader(TOKEN_HEADER);
if (StringUtils.isEmpty(token) || !token.startsWith(TOKEN_PREFIX)){
String token = request.getHeader(JWTConstants.TOKEN_HEADER);
if (StringUtils.isEmpty(token) || !token.startsWith(JWTConstants.TOKEN_PREFIX)){
chain.doFilter(request,response);
return;
}
......@@ -83,9 +74,9 @@ public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
// 这里从token中获取用户信息并新建一个token
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws ParseException, JOSEException {
String token = tokenHeader.replace(TOKEN_PREFIX, "");
String token = tokenHeader.replace(JWTConstants.TOKEN_PREFIX, "");
SignedJWT jwt = SignedJWT.parse(token);
JWSVerifier verifier = new MACVerifier(SECRET);
JWSVerifier verifier = new MACVerifier(JWTConstants.SECRET);
//校验是否有效
if (!jwt.verify(verifier)) {
throw new AccountExpiredException("Token 无效");
......
package com.microservice.skeleton.auth.service.impl;
import com.microservice.skeleton.auth.entity.AuthUser;
import com.microservice.skeleton.auth.service.UpmsService;
import com.microservice.skeleton.common.util.StatusCode;
......@@ -8,9 +9,7 @@ import com.microservice.skeleton.common.vo.RoleVo;
import com.microservice.skeleton.common.vo.UserVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
......@@ -58,7 +57,7 @@ public class UserDetailsServiceImpl implements UserDetailsService {
}
}
AuthUser user = new AuthUser(userVo.getUsername(), userVo.getPassword(), grantedAuthorities);
user.setUserId(userVo.getId());
user.setId(userVo.getId());
return user;
}
}
......@@ -9,6 +9,10 @@ spring:
nacos:
discovery:
server-addr: 127.0.0.1:8848
jackson:
date-format: yyyy-MM-dd HH:mm:ss
locale: zh_CN
time-zone: GMT+8
logging:
level:
root: INFO
......
......@@ -15,6 +15,10 @@ spring:
nacos:
discovery:
server-addr: 127.0.0.1:8848
jackson:
date-format: yyyy-MM-dd HH:mm:ss
locale: zh_CN
time-zone: GMT+8
mybatis:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册