OAuth2和JWT案例

案例分析

在本案例中有3个工程,分别为 eureka- server、auth- service和user- service其中auth- service和 user-service向 eureka- server注册服务。auth- service负责授权,授权需要用户提供客户端的clientId和 password,以及授权用户的 username和 password这些信息准备无误之后,auth- - service返回JWT,该JWT包含了用户的基本信息和权限点信息,并通过RSA加密。 user-service作为资源服务,它的资源已经被保护起来了,需要相应的权限才能访问。user-service 服务得到用户请求的JWT后,先通过公钥解密JWT,得到该JWT对应的用户的信息和用户的权限信息,再判断该用户是否有权限访问该资源。

其中,在user-service服务的登录API接口(登录API接口不受保护)中,当用户名和密码验证正确后,通过远程调用向auth-service获取JWT,并返回JWT给用户。用户获取到JWT之后,以后的每次请求都需要在请求头中传递该JWT,从而资源服务能够根据JWT来进行权限验证。架构图如图:
在这里插入图片描述
编写主Maven工程

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>oauth2-jwt-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Dalston.SR1</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

编写Eureka Server:
在主Maven工程下,创建一个eureka-server的Module工程,作为服务注册中心的工程。在工程的pom文件引入相应的依赖,包括继承了主Maven工程的pom 文件, 并引入EurekaServer的起步依赖,代码如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
</dependencies>

在工程的配置文件application.yml中,配置程序的端口号为8761,并配置不自注册,配置代码如下:

server:
  port: 8761

spring:
  application:
    name: eureka-server

eureka:
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url: 
      defaultZone: http://localhost:${server.port}/eureka/

启动类:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
编写Uaa授权服务

1.引入依赖:
在主Maven工程下新建一个 Module工程,取名为uaa-service。工程的pom文件继承了主Maven工程的pom文件,并引入工程所需的依赖,包括连接数据库的依赖mysql-connectorjava和JPA的起步依赖spring-boot-starter-data-jpa、Web 的起步依赖spring-boot-starter- web、Eureka客户端的起步依赖spring-cloud-starter eureka,以及Spring Cloud OAuth2 的起步依赖spring-cloud-starter- oauth2。

其中,Spring Cloud 0Auth2的起步依赖包含了Spring Security 0Auth2和Spring SecurityJWT的等依赖,代码如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

2.配置文件:
在程序的配置文件aplication.yml中配置程序的名称为uaa-service, 端口号为9999,以及连接数据库驱动、JPA 的配置和服务的注册地址,代码如下:

server:
  port: 9999

spring:
  application:
    name: uaa-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka/

3.配置Spring Security:
uaa-service服务对外提供获取JWT的API接口,uaa-service 服务是一个授权服务器,同时也是资源服务器,需要配置该服务的SpringSecurity,配置代码如下:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -> {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                })
                .and()
                .authorizeRequests()
                .antMatchers("/**").authenticated()
                .and()
                .httpBasic();
    }
    
    @Autowired
    UserServiceDetail userServiceDetail;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userServiceDetail)
                .passwordEncoder(new BCryptPasswordEncoder());
    }
}

在上面的配置类中,通过@EnableWebSecurity注解开启Web资源的保护功能。在configure(HttpSecurity http)方法中配置所有的请求都需要验证,如果请求验证不通过,则重定位到401的界面。在configure(AuthenticationManagerBuilder auth)方法中配置验证的用户信息源、密码加密的策略。向IoC容器注入AuthenticationManager对象的Bean,该Bean在OAuth2的配置中使用,因为只有在0Auth2中配置了AuthenticationManager,密码类型的验证才会开启。在本案例中,采用的是密码类型的验证。

采用BCryptPasswordEncoder对密码进行加密,在创建用户时,密码加密也必须使用这个类。使用了UserServiceDetail 这个类,实现了UserDetailsService接口,代码如下:

@Service
public class UserServiceDetail implements UserDetailsService {

    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userDao.findByUsername(username);
    }
}

UserDao:

public interface UserDao extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

4.配置Authorization Server:

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("user-service")
                .secret("123456")
                .scopes("service")
                .authorizedGrantTypes("refresh_token", "password")
                .accessTokenValiditySeconds(3600);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtTokenEnhancer())
                .authenticationManager(authenticationManager);
    }
    
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
    
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
    }
    
    @Bean
    protected JwtAccessTokenConverter jwtTokenEnhancer() {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("fzp-jwt.jks"), "fzp123".toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("fzp-jwt"));
        return converter;
    }
}

在上面的配置代码中,OAuth2Config类继承了AuthorizationServerConfigurerAdapter类,并在OAuth2Config类加上@EnableAuthorizationServer注解,开启Authorization Server的功能。作为Authorization Server 需要配置两个选项,即ClientDetailsServiceConfigurer 和AuthorizationServerEndpointsConfigurer。

其中,ClientDetailsServiceConfigurer配置了客户端的一些基本信息,clients.inMemory()方法是将客户端的信息存储在内存中,.withClient( "user- service")方法创建了一一个 ClientId 为“user-service的客户端,.authorizedGrantTypes("refresh_ token", "password")方 法配置类验证类型为refresh token和password, .scopes("service ")方法配置了客户端域为“service”, .accessTokenValiditySeconds(3600)方法配置了Token的过期时间为3600秒。

AuthorizationServerEndpointsConfigurer配置了tokenStore 和authenticationManager.其中tokenStore使用JwtTokenStore,JwtTokenStore 并没有做任何存储,tokenStore 需要一个JwtAccessTokenConverter对象,该对象用于Token转换。本案例中使用了非对称性加密RSA对JWT进行加密。

authenticationManager需要配置AuthenticationManager 这个Bean, 这个Bean 来源于WebSecurityConfigurerAdapter的配置,只有配置了这个Bean才会开启密码类型的验证。

5.生成jks文件:
在AuthorizationServerEndpointsConfigurer的配置中,配置JwtTokenStore 时需要使用jks文件作为Token加密的秘钥。那么jks文件是怎样生成的呢?在本案例中,jks文件是使用Javakeytool生成的,在生成jks 文件之前需要保证Jdk已经安装。打开计算机终端,输入以下命令:

keytool -genkeypair -alias fzp-jwt -validity 3650 -keyalg RSA -dname "CN=jwt,OU=jtw,O=jtw,L=zurich,S=zurich,C=CH" -keypass fzp123 -keystore fzp-jwt.jks -storepass fzp123

在上面的命令中,-alias 选项为别名,-keypass 和-storepass为密码选项,-validity 为配置jks文件的过期时间(单位:天)。
获取的jks文件作为私钥,只允许Uaa服务持有,并用作加密JWT。那么user-serive这样的公钥。获取jks文件的公钥命令如下:

keytool -list -rfc --keystore fzp-jwt.jks | openssl x509 -inform pem -pubkey

在计算机终端输入上面的命令,提示需要密码,本例的密码为“fzp123”, 输入即可,显示的公钥信息如下:
在这里插入图片描述
新建-一个public.cert 文件,将上面的公钥信息复制到public.cert 文件中并保存。并将public.cert文件放在资源服务的工程的Resource目录下。到目前为止,Uaa授权服务已经搭建完毕。

需要注意的是,Maven 在项目编译时,可能会将jks文件编译,导致jks文件乱码,最后不可用。需要在工程的pom文件中添加以下内容:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>cert</nonFilteredFileExtension>
                    <nonFilteredFileExtension>jks</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>
    </plugins>
</build>
编写user-service资源服务:

1.依赖:
user-service工程的pom文件继承了主Maven工程的pom文件。在user- service工程的pom文件中引入Web功能的起步依赖spring botater-web、OAuth2 的起步依赖springecloud-starter-oauth2、数据库连接依赖mysl-connectorjava、 JPA的起步依赖spring bootateredataja、Eureka的起步依赖spring-loud-starter-eureka和声明式调用Feign和Hystrix的起步依赖。user-service工程的 pom文件代码如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
</dependencies>

2. 配置文件:
在工程的配置文件application.yml 中,配置程序名为user-service, 端口号为9090,服务的注册地址为htp://localhost:/8761/eureka/, 以及连接的数据库的地址、用户名、密码和JPA的相关配置。另外,需要配置feign.hystrix.enable为true,即开启Feign的Hystrix功能。完整的配置代码如下:

server:
  port: 9090

spring:
  application:
    name: user-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
      
feign:
  hystrix:
    enabled: true

3. 配置Resource Server:
在配置Resource Server之前,需要注入JwtTokenStore类型的Bean。建一个 JwtConfig类,加上@Configuration注解,开启配置文件的功能。JwtTokenStore 类型的Bean 需要配置- -个JwtAccessTokenConverter类型的Bean,该Bean用作JWT转换器。JwtAccessTokenConverter需要设置VerifierKey, VerifierKey 为公钥,存放在Resource 目录下的public.cert 文件中。JwtConfig类的代码如下:

@Configuration
public class JwtConfig {
    @Autowired
    JwtAccessTokenConverter jwtAccessTokenConverter;
    
    @Bean
    @Qualifier("tokenStore")
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }
    
    @Bean
    protected JwtAccessTokenConverter jwtTokenEnhancer() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.cert");
        String publicKey = null;
        try {
            publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        converter.setVerifierKey(publicKey);
        return converter;
    }
}

然后配置Resource Server, 新建一-个ResourceServerConfig 的类,该类继承了Resource-ServerConfigurerAdapter类,在ResourceServerConfig类上加@EnableResourceServer注解,开启Resource Server功能。作为Resource Server,需要配置HttpSecurity和ResourceServerSecurityConfigurer这两个选项。

HttpSecurity 配置了哪些请求需要验证,哪些请求不需要验证。在本案例中,“/user/login" (登录)和“/user/register" (注册)两个API接口不需要验证,其他请求都需要验证。ResourceServerSecurityConfigurer需要配置tokenStore, tokenStore为之前注入IoC容器中的tokenStore。代码如下:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
    @Autowired
    TokenStore tokenStore;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/user/login", "/user/register").permitAll()
                .antMatchers("/**").authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore);
    }
}

4. 配置Spring Security:
新建一个配置类GlobalMethodSecurityConfig, 在此类中通过@EnableGlobalMethodSecurity(prePostEnabled = true)注解开启方法级别的安全验证。代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodSecurityConfig {
}

5. 编写用户注册接口:
这里用到了User和Role两个实体类,这两个类的代码和上面是一样的,在此不再重复。Dao 层的UserDao类继承了JpaRepository 类,并有一一个根据用户名获取用户的方法,代码如下:

public interface UserDao extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

Service层的UserService写一一个插入用户的方法,代码如下:

@Service
public class UserServiceDetail {
    
    @Autowired
    UserDao userDao;
    
    public User insertUser(String username, String password) {
        User u = new User();
        u.setUsername(username);
        u.setPassword(BPwdEnchoderUtil.BCrytPassword(password));
        return userDao.save(u);
    }
}

在UserServiceDetail类中使用到了工具类BPwdEncoderUtil,其中BCryptPasswordEncoder是Spring Security的加密类,BPwdEncoderUtil 类的代码如下:

public class BPwdEnchoderUtil {
    private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

    public static String BCrytPassword(String password) {
        return encoder.encode(password);
    }

    public static boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encoder.matches(rawPassword, encodedPassword);
    }
}

在Web层,在UserController中写一个注册的API接口“/user/register”, 代码如下:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    UserServiceDetail userServiceDetail;
    
    @PostMapping("/register")
    public User postUser(String username, String password) {
        //省略参数判断...
        return userServiceDetail.insertUser(username, password);
    }
}

启动所有工程:注册一个账号:

curl -d "username=tom&password=123456" "localhost:9090/user/register"

返回信息:

{"id":4,"username":"tom","password":"$2a$10$hf/bQViPFaAH6I8qblTNLu2zaIVs/9q6.IEHbfDNmv.kLLByCXPlS","authorities":null,"enabled":true,"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true}%

使用postman:
在这里插入图片描述

6.编写用户登录接口:
在Service层中,在UserServiceDetail中添加一一个login ( 登录)方法,代码如下:

@Service
public class UserServiceDetail {

    @Autowired
    UserDao userDao;

    public User insertUser(String username, String password) {
        User u = new User();
        u.setUsername(username);
        u.setPassword(BPwdEnchoderUtil.BCrytPassword(password));
        return userDao.save(u);
    }

    @Autowired
    AuthServiceClient authServiceClient;

    public UserLoginDTO login(String username, String password) {
        User user = userDao.findByUsername(username);
        if (null == user) {
            throw new UserLoginException("error username");
        }
        if (!BPwdEnchoderUtil.matches(password, user.getPassword())) {
            throw new UserLoginException("error password");
        }
        //user-service:123456
        JWT jwt = authServiceClient.getToken("Basic dXNlci1zZXJ2aWNlOjEyMzQ1Ng==", "password", username, password);
        if (jwt == null) {
            throw new UserLoginException("error internal");
        }
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setJwt(jwt);
        userLoginDTO.setUser(user);
        return userLoginDTO;
    }
}

其中,AuthServiceClient为Feign的客户端,所以需要程序的启动类UserServiceApplication通过@EnableFeignClient开启Feign客户端的功能:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

AuthServiceClient通过向uaa-service 服务远程调用“/oauth/token” API 接口,获取JWT。在“/oauth/token"API接口中需要在请求头传入Authorization信息,并需要传请求参数认证类型grant type、用户名username和密码password,代码:

@FeignClient(value = "uaa-service", fallback = AuthServiceHystrix.class)
public interface AuthServiceClient {

    @PostMapping("/oauth/token")
    JWT getToken(@RequestHeader("AUthorization")String authorization, @RequestParam("grant_type")String type,
                 @RequestParam("username")String username, @RequestParam("password")String password);
}

其中,AuthServiceHystrix为AuthServiceClient的熔断器:

@Component
public class AuthServiceHystrix implements AuthServiceClient{
    @Override
    public JWT getToken(String authorization, String type, String username, String password) {
        return null;
    }
}

JWT为一个JavaBean,它包含了access_toke、token_type和refresh_token等信息:

public class JWT {
    private String access_token;
    private String token_type;
    private String refresh_toke;
    private int expires_in;
    private String scope;
    private String jti;
    //省略getter setter...
}

UserLoginDTO包含了一个User和一个JWT对象,用于返回数据的实体:

public class UserLoginDTO {
    private JWT jwt;
    private User user;
    //...
}

登录异常类UserLoginException,继承自RuntimeException:

public class UserLoginException extends RuntimeException {
    public UserLoginException(String message) {
        super(message);
    }
}

异常统一处理类为ExceptionHandle 类,在该类中加上@ControllerAdvice注解表明该类是一个异常统一处理类。 通过@ExceptionHandler注解配置了统一处理 UserLoginException类的异常方法,统一返回了异常的message信息,代码如下:

@ControllerAdvice
@ResponseBody
public class ExceptionHandle {
    @ExceptionHandler(UserLoginException.class)
    public ResponseEntity<String> handleException(Exception e) {
        return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
    }
}

在Web层的UserController类写一个登录的API接口"/user/login":

@PostMapping("/login")
public UserLoginDTO login(@RequestParam("username")String username, @RequestParam("password")String password) {
    //省略参数判断...
    return userServiceDetail.login(username, password);
}

在“/user/login"API接口中,需要的请求参数为用户名和密码。首先会根据用户名查询数据库,获取用户,如果用户存在, 判断密码是否正确。如果密码正确,通过Feign客户端远程调用uaa-service,获取JWT,获取成功,将用户和JWT封装成UserLoginDTo对象返回。现在使用Curl调用登录API接口,执行命令如下:

curl user-service:123456@localhost:9999/oauth/token -d grant_type=password -d username=tom -d password=123456

在这里插入图片描述
在这里插入图片描述
测试:
编写一个"/foo"的API接口,该API接口需要"ROLE_ADMIN"权限才能访问:

@RestController
@RequestMapping("/foo")
public class WebController {
    @GetMapping()
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public String getFoo() {
        return "i'm foo, " + UUID.randomUUID().toString();
    }
}

以username为“tom”,密码为“123456”登录,登录成功后返回了JWT对象,JWT中有一个access_ token 的字符串。将该Token放在请求头重中,进行请求,如下:

在这里插入图片描述

从上面的返回信息可知,该用户没有权限访问该API接口。这是正常的,因为新注册的“tom”这个用户并没有“ROLE_ADMIN"权限。为了方便演示,现在给“ tom”这个用户赋予“ROLE_ADMIN"的权限,直接在数据库中插入以下数据,数据库脚本如下:

insert into `role` values('1','ROLE_USER'),('2','ROLE_ADMIN');
insert into `user_role` values('5', '2');

重新登录刷新token,再重新访问"/foo":
在这里插入图片描述

总结

在本案例中,用户通过登录接口来获取授权服务的Token。用户获取Token成功后,在以后每次访问资源服务的请求中都需要携带该Token。资源服务通过公钥解密Token,解密成功后可以获取用户信息和权限信息,从而判断该Token所对应的用户是谁,具有什么权限。

这个架构的优点在于,一次获取Token,多次使用,不再每次询问Uaa服务该Token所对应的用户信息和用户的权限信息。这个架构也有缺点,例如一旦用户的权限发生了改变,该Token中存储的权限信息并没有改变,需要重新登录获取新的Token。就算重新获取了Token,如果原来的Token没有过期,仍然是可以使用的,所以需要根据具体的业务场景来设置Token的过期时间
一种改进方式是将登录成功后获取的Token缓存在网关上,如果用户的权限更改,将网关上缓存的Token删除。当请求经过网关,判断请求的Token在缓存中是否存在,如果缓存中不存在该Token,则提示用户重新登录。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值