Spring Cloud下实现用户鉴权的方案

yizhihongxing

下面我将为大家详细讲解“Spring Cloud下实现用户鉴权的方案”的完整攻略。本攻略分为以下几个部分:

  1. Spring Cloud微服务架构
  2. 鉴权的基本概念
  3. 用户鉴权的实现方案
  4. 示例一:使用JWT实现用户鉴权
  5. 示例二:使用OAuth2实现用户鉴权

1. Spring Cloud微服务架构

Spring Cloud是基于Spring Boot的微服务开发框架。它提供了丰富的库和工具来简化微服务的开发和部署。Spring Cloud包含了一系列的子项目,例如服务发现、负载均衡、配置中心等等。微服务架构通过将应用拆分为小型服务来提高系统的灵活性和可扩展性。

2. 鉴权的基本概念

鉴权是指验证用户的身份是否合法,并决定用户是否有权执行某些操作。在微服务架构中,通常使用token来实现鉴权。当用户登录成功后,系统会生成一个token并返回给用户。用户再次请求时,需要将token携带在请求头中,服务端通过验证token来判断用户的身份是否合法。

3. 用户鉴权的实现方案

在Spring Cloud中,我们可以通过对网关(Gateway)或具体的服务进行配置,来实现用户鉴权。常用的实现方案有以下几种:

  • JWT(Json Web Token)
  • OAuth2
  • Spring Security

在这里我们将重点介绍JWT和OAuth2的实现方案。

4. 示例一:使用JWT实现用户鉴权

JWT是一种轻量级的鉴权方式。它将用户信息编码为一个加密的token,并在每次请求时携带这个token。服务端通过解析token来验证用户的身份。下面是基于Spring Cloud下使用JWT实现用户鉴权的一般步骤:

  1. 添加依赖:
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
  1. 实现JWT工具类:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "iat";

    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 根据用户名生成token
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, username);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 解析token,获取用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 验证token是否有效
     */
    public boolean validateToken(String token) {
        return !isTokenExpired(token);
    }

    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    private boolean isTokenExpired(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }
}
  1. 实现过滤器:
import io.jsonwebtoken.ExpiredJwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

@Component
public class AuthFilter extends AbstractGatewayFilterFactory<AuthFilter.Config> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter.class);

    @Value("${jwt.header}")
    private String header;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private List<String> ignoreUrls;

    public AuthFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilterFactory<Config> apply(Config config) {
        return (exchange, chain) -> doFilter(exchange, chain, config);
    }

    private Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain, Config config) {
        String requestPath = exchange.getRequest().getPath().value();
        for (String ignoreUrl : ignoreUrls) {
            if (requestPath.startsWith(ignoreUrl)) {
                return chain.filter(exchange);
            }
        }
        String token = exchange.getRequest().getHeaders().getFirst(header);
        if (token == null || token.isEmpty()) {
            LOGGER.warn("token is empty");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        try {
            boolean tokenValid = jwtTokenUtil.validateToken(token);
            if (tokenValid) {
                String username = jwtTokenUtil.getUsernameFromToken(token);
                Map<String, Object> attrib = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_ATTRIBUTES);
                Map<String, Object> headers = (Map<String, Object>) attrib.get("headers");
                headers.put("X-Forwarded-User", username);
                return chain.filter(exchange);
            } else {
                LOGGER.warn("token is invalid");
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
        } catch (ExpiredJwtException e) {
            LOGGER.warn("token is expired");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        } catch (Exception e) {
            LOGGER.error("auth error:", e);
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            return exchange.getResponse().setComplete();
        }
    }

    @Order(-500)
    public static class Config {
    }
}
  1. 在配置文件中添加必要的配置:
# JWT配置
jwt:
  secret: mySecret
  expiration: 3600
  header: Authorization
# 忽略鉴权的URL
ignoreUrls:
  - /user/login

5. 示例二:使用OAuth2实现用户鉴权

OAuth2是一种标准的开放授权协议。它提供了一种方式,允许第三方应用程序访问用户在其他应用程序上存储的资源。在Spring Cloud下,可以使用Spring Security OAuth2来实现用户鉴权。下面是基于Spring Cloud下使用OAuth2实现用户鉴权的步骤:

  1. 添加依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
  1. 配置文件中添加必要配置:
# auth2配置
security:
  oauth2:
    client:
      client-id: myClientId
      client-secret: myClientSecret
      access-token-uri: http://localhost:8080/oauth/token
      user-authorization-uri: http://localhost:8080/oauth/authorize
      redirect-uri-template: {baseUrl}/login/oauth2/code/{registrationId}
      scope: openid,profile,email
      client-authentication-method: post
  1. 配置过滤器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

@Component
public class AuthFilter extends AbstractGatewayFilterFactory<AuthFilter.Config> {

    @Autowired
    private OAuth2AuthorizedClientService clientService;

    @Autowired
    private List<String> ignoreUrls;

    public AuthFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilterFactory<Config> apply(Config config) {
        return (exchange, chain) -> doFilter(exchange, chain, config);
    }

    private Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain, Config config) {
        String requestPath = exchange.getRequest().getPath().value();
        for (String ignoreUrl : ignoreUrls) {
            if (requestPath.startsWith(ignoreUrl)) {
                return chain.filter(exchange);
            }
        }
        OAuth2AuthorizedClient client = getClient(exchange);
        if (client == null) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        OAuth2User user = client.getPrincipal();
        Map<String, Object> attrib = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_ATTRIBUTES);
        Map<String, Object> headers = (Map<String, Object>) attrib.get("headers");
        headers.put("X-Forwarded-User", user.getName());
        return chain.filter(exchange);
    }

    private OAuth2AuthorizedClient getClient(ServerWebExchange exchange) {
        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
        if (queryParams.containsKey("code")) {
            String code = queryParams.getFirst("code");
            return clientService.loadAuthorizedClientByAuthorizationCode("myRegistrationId", code);
        }
        return null;
    }

    @Order(-500)
    public static class Config {
    }
}

以上就是使用OAuth2实现用户鉴权的步骤。

希望这篇攻略可以帮助你更好的理解Spring Cloud下实现用户鉴权的方案。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Cloud下实现用户鉴权的方案 - Python技术站

(0)
上一篇 2023年6月3日
下一篇 2023年6月3日

相关文章

  • tomcat共享多个web应用会话的实现方法

    实现多个Web应用共享会话的方法有很多,而在Tomcat中,也存在不同的实现方式。下面将详细讲解几种可行的方案。 方案一:使用Tomcat的内置共享会话功能 Tomcat自身具备相应的共享会话功能,可以通过修改配置文件来启用该功能。首先,在Tomcat安装目录下找到conf/context.xml文件,在其中添加以下配置: <Valve classNa…

    Java 2023年6月15日
    00
  • SpringBoot加载bean的八种方式总结

    SpringBoot加载Bean的八种方式总结 在使用SpringBoot时,我们常常需要加载Bean来完成各种任务。SpringBoot提供了多种方式来加载Bean,本文将对其进行总结。 1. 使用@ComponentScan自动扫描注解 @ComponentScan是Spring框架中常用的注解,它会自动扫描指定的包及其子包,将所有标记有@Compone…

    Java 2023年5月19日
    00
  • Java正则表达式处理特殊字符转义的方法

    当我们使用Java正则表达式处理字符串时,需要注意特殊字符的转义问题。下面是处理特殊字符转义的方法,包括两个示例说明: 1.将特殊字符用反斜杠转义 在Java正则表达式中,一些特殊字符具有特殊含义,比如.需要匹配除了换行符以外的任意字符,而不是只匹配.字符。所以,我们需要使用反斜杠(\)将这些特殊字符转义,表示想要匹配这些特殊字符而非具有特殊含义。 例如,要…

    Java 2023年5月27日
    00
  • SpringBoot Mail邮件任务详情

    Spring Boot Mail邮件任务详情 在Spring Boot中,我们可以使用Mail模块来实现邮件发送功能。本文将详细讲解Spring Boot Mail邮件任务的完整攻略,并提供两个示例。 1. 配置邮件发送信息 以下是配置邮件发送信息的基本流程: 在application.properties或application.yml文件中添加以下内容:…

    Java 2023年5月15日
    00
  • Spring Boot 2.x 把 Guava 干掉了选择本地缓存之王 Caffeine(推荐)

    下面我将详细讲解 Spring Boot 2.x 把 Guava 干掉了选择本地缓存之王 Caffeine(推荐)的攻略。 背景 在 Spring Boot 2.x 版本中,默认使用的是 Caffeine 作为本地缓存框架,而在之前的版本中,默认使用的是 Guava,这是因为,Caffeine 有更好的性能和更多的特性。 步骤 下面是使用 Caffeine …

    Java 2023年5月20日
    00
  • Maven搭建springboot项目的方法步骤

    下面我将详细讲解如何使用Maven搭建Spring Boot项目的方法步骤。 1. 准备工作 在使用Maven进行项目构建前,首先需要在本地安装和配置Maven环境。可以根据官方文档进行下载和安装,也可以使用包管理工具进行安装,例如使用APT工具: sudo apt-get install maven 2. 创建Spring Boot项目 2.1 使用Spr…

    Java 2023年5月15日
    00
  • Android AndBase框架使用封装好的函数完成Http请求(三)

    【标题】 Android AndBase框架使用封装好的函数完成Http请求(三)完整攻略 【内容】 本文介绍如何使用AndBase框架中封装好的函数完成Http请求,包括GET请求、POST请求和文件上传等。具体实现过程如下: 添加AndBase框架依赖库 在项目的build.gradle文件中添加AndBase的依赖库: dependencies { i…

    Java 2023年6月15日
    00
  • Java掩码的几种使用例举

    Java掩码的几种使用例举 在Java中,掩码的主要作用是用来过滤或者匹配不同的字节位。掩码是用位运算符来创建的。在Java中,我们可以使用按位与、或、异或等位运算符来创建掩码。 按位与掩码 按位与掩码是将每个位分别与操作数进行运算,返回新的结果。当操作数均为1的时候,该位的掩码返回1,否则返回0。在Java中,我们可以使用“&”符号来表示按位与掩码…

    Java 2023年5月29日
    00
合作推广
合作推广
分享本页
返回顶部