网站怎么做rss/怎样申请网站
这里我们一共要建三个包
- auth-service-api:用户登录、鉴权和刷新token的接口层
- auth-service:接口的实现,以及jwt的引用
- gateway:网关集成jwt鉴权
auth-service-api
项目结构如下:
- pom
<artifactId>auth-service-api</artifactId>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
</dependencies>
- Account
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Account implements Serializable {private String username;private String token;private String refreshToken;
}
- AuthResponseCode
这里放一些响应代码
public class AuthResponseCode {public static final Long SUCCESS = 1L;public static final Long INCORRECT_PWD = 1000L;public static final Long USER_NOT_FOUND = 1001L;}
- AuthResponse
定义一个响应VO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthResponse {private Account account;private Long code;}
- AuthService
@FeignClient("auth-service")
public interface AuthService {@PostMapping("/login")@ResponseBodypublic AuthResponse login(@RequestParam("username") String username,@RequestParam("password") String password);@GetMapping("/verify")public AuthResponse verify(@RequestParam("token") String token,@RequestParam("username") String username);@PostMapping("/refresh")@ResponseBodypublic AuthResponse refresh(@RequestParam("refresh") String refresh);}
auth-service
项目结构如下:
- 配置文件
spring.application.name=auth-service
server.port=65100eureka.client.serviceUrl.defaultZone=http://localhost:20000/eureka/logging.file=${spring.application.name}.logspring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379info.app.name=auth-service
info.app.description=testmanagement.security.enabled=false
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
- pom
<artifactId>auth-service</artifactId>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- jwt --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.7.0</version></dependency><dependency><groupId>com.xxx</groupId><artifactId>auth-service-api</artifactId><version>${project.version}</version></dependency></dependencies>
- AuthApplication启动类
@SpringBootApplication
@EnableEurekaClient
public class AuthApplication {public static void main(String[] args) {new SpringApplicationBuilder(AuthApplication.class).web(WebApplicationType.SERVLET).run(args);}
}
- JwtService
@Slf4j
@Service
public class JwtService {private static final String KEY = "testKey";private static final String ISSUER = "Lyn";private static final long TOKEN_EXP_TIME = 60 * 1000;private static final String USER_NAME = "username";/*** 生成Token*/public String token(Account acct) {Date now = new Date();Algorithm algorithm = Algorithm.HMAC256(KEY);String token = JWT.create().withIssuer(ISSUER).withIssuedAt(now).withExpiresAt(new Date(now.getTime() + TOKEN_EXP_TIME)).withClaim(USER_NAME, acct.getUsername()).sign(algorithm);log.info("jwt generated user={}", acct.getUsername());return token;}/*** 校验Token*/public boolean verify(String token, String username) {log.info("verifying jwt - username={}", username);try {Algorithm algorithm = Algorithm.HMAC256(KEY);JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).withClaim(USER_NAME, username).build();verifier.verify(token);return true;} catch (Exception e) {log.error("auth failed", e);return false;}}}
- controller
@RestController
@Slf4j
public class Controller {@Autowiredprivate JwtService jwtService;@Autowiredprivate RedisTemplate redisTemplate;@PostMapping("/login")@ResponseBodypublic AuthResponse login(@RequestParam String username,@RequestParam String password) {Account account = Account.builder().username(username).build();// 这里应该验证username + password,我们跳过验证String token = jwtService.token(account);account.setToken(token);account.setRefreshToken(UUID.randomUUID().toString());redisTemplate.opsForValue().set(account.getRefreshToken(), account);return AuthResponse.builder().account(account).code(SUCCESS).build();}@PostMapping("/refresh")@ResponseBodypublic AuthResponse refresh(@RequestParam String refreshToken) {Account account = (Account) redisTemplate.opsForValue().get(refreshToken);if (account == null) {return AuthResponse.builder().code(USER_NOT_FOUND).build();}String jwt = jwtService.token(account);account.setToken(jwt);account.setRefreshToken(UUID.randomUUID().toString());redisTemplate.delete(refreshToken);redisTemplate.opsForValue().set(account.getRefreshToken(), account);return AuthResponse.builder().account(account).code(SUCCESS).build();}@GetMapping("/verify")public AuthResponse verify(@RequestParam String token,@RequestParam String username) {boolean success = jwtService.verify(token, username);return AuthResponse.builder().code(success ? AuthResponseCode.SUCCESS : AuthResponseCode.USER_NOT_FOUND).build();}
}
gateway
gateway的配置可参考《SpringBoot:Gateway引入初体验》,我们要做以下改造
- pom引入
auth-service-api
<!-- 因为spring cloud gateway是基于webflux的,
如果非要web支持的话需要导入spring-boot-starter-webflux而不是spring-boot-start-web-->
<dependency><groupId>com.xxx</groupId><artifactId>auth-service-api</artifactId><version>${project.version}</version><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></exclusion></exclusions>
</dependency>
我们配置一个自定义Filter
@Component("authFilter")
@Slf4j
public class AuthFilter implements GatewayFilter, Ordered {private static final String AUTH = "Authorization";private static final String USERNAME = "user-name";@Autowiredprivate AuthService authService;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("Auth start");ServerHttpRequest request = exchange.getRequest();HttpHeaders header = request.getHeaders();String token = header.getFirst(AUTH);String username = header.getFirst(USERNAME);ServerHttpResponse response = exchange.getResponse();if (StringUtils.isBlank(token)) {log.error("token not found");response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}AuthResponse resp = authService.verify(token, username);if (resp.getCode() != 1L) {log.error("invalid token");response.setStatusCode(HttpStatus.FORBIDDEN);return response.setComplete();}// TODO 将用户信息存放在请求header中传递给下游业务ServerHttpRequest.Builder mutate = request.mutate();mutate.header("user-name", username);ServerHttpRequest buildRequest = mutate.build();// TODO 如果响应中需要放数据,也可以放在response的header中response.setStatusCode(HttpStatus.OK);response.getHeaders().add("username",username);return chain.filter(exchange.mutate().request(buildRequest).response(response).build());}@Overridepublic int getOrder() {return 0;}
}
然后在过滤器配置中直接使用AuthFilter
@Configuration
public class GatewayConfiguration {@Autowiredprivate AuthFilter authFilter;@Bean@Orderpublic RouteLocator customizedRoutes(RouteLocatorBuilder builder) {return builder.routes().route(r -> r.path("/java/**").filters(f -> f.stripPrefix(1).filter(authFilter)).uri("lb://FEIGN-CLIENT")).build();}}
到这里,使用Gateway + JWT 实现微服务体系下的用户鉴权的基础逻辑就完成了!