上一篇文章我们集成了服务网关Spring Cloud Gateway,所有的服务请求都可以通过Gateway访问。那我们就可在服务网关这一层对用户的请求进行鉴权,判断是否可以访问路由的API接口。
加下来我们开始增加鉴权,这里我们使用jwt
按照第二篇文章创建一个module,起名为app-auth。
1.8
Hoxton.SR1
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
2.6.1
com.auth0
java-jwt
3.10.0
cn.hutool
hutool-all
5.2.0
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
@SpringBootApplication
@EnableEurekaClient // 开启eureka客户端模式
public class AppAuthApplication {
public static void main(String[] args) {
SpringApplication.run(AppAuthApplication.class, args);
}
}
这里我们将生成的token和refreshToken都存储到redis中,所以我们也引入了redis依赖。为了保证一定的安全性,我们的token和refreshToken都是有时效的,这里我们将这两个值的有效时间放到配置文件中,以便灵活修改。
在生成token的时候,我们将账号和客户端类型传入,客户端类型主要分为,网页浏览器端,手机app端和桌面程序端。三端都有各自的token。
// 生成token和refreshToken
public Map getToken(String phone, String type){
//生成refreshToken
String refreshToken = UUID.randomUUID().toString().replaceAll("-","");
String token = this.buildJWT(phone, type);
String key = SecureUtil.md5(type + phone);
//向hash中放入数值
stringRedisTemplate.opsForHash().put(key,"token", token);
stringRedisTemplate.opsForHash().put(key,"refreshToken", refreshToken);
//设置key过期时间
stringRedisTemplate.expire(key, refreshTokenExpireTime, TimeUnit.MILLISECONDS);
Map map = new HashMap<>(2);
map.put("token", token);
map.put("refreshToken", refreshToken);
return map;
}
@GetMapping(value = "/login")
public ResponseDTO
此处暂时只展示登录方法,刷新token方法和退出登录方法请查看git仓库。
此处代码较多,只展示部分核心代码
创建RequestGlobalFilter类,增加@Component注解,实现GlobalFilter, Ordered这两个接口;实现public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) 方法
由于这个Filter拦截所有请求,所以我们要提前定义一下不拦截的接口,例如登录接口和刷新token的接口。我们在server-gateway的配置文件中增加ignore.urls,把不拦截的接口用逗号分隔的方式放到这个配置上,例如:
ignore.urls=/auth/login,/authrefresh
对于需要拦截的url,我们需要从header中拿到token和clientType,然后通过jwt工具进行校验该token是否有效,如果无效返回401错误信息。
private String verifyJWT(String token, String clientType) {
String userPhone;
try {
Algorithm algorithm = Algorithm.HMAC256(clientType);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("userPhone")
.build();
DecodedJWT jwt = verifier.verify(token);
userPhone = jwt.getClaim("phone").asString();
} catch (JWTVerificationException e) {
return "";
}
return userPhone;
}
在此处我们需要引入jwt依赖
com.auth0
java-jwt
3.10.0
# 路由到app-auth服务
spring.cloud.gateway.routes[2].id=app-auth
spring.cloud.gateway.routes[2].uri=lb://APP-AUTH
spring.cloud.gateway.routes[2].predicates[0]=Path=/auth/**
spring.cloud.gateway.routes[2].filters[0]=StripPrefix=1
分别启动server-eureka,server-gateway,app-auth,app-order,app-storage
由于本文涉及的代码偏多,请大家移步至git仓库查看更多代码。
https://gitee.com/hedavid/spring-cloud-example