用Spring Cloud OAuth2和JWT保護微服務


采用Spring Security AOuth2 和 JWT 的方式,避免每次請求都需要遠程調度 Uaa 服務。采用Spring Security OAuth2 和 JWT 的方式,Uaa 服務只驗證一次,返回JWT。返回的 JWT 包含了用戶的所有信息,包括權限信息。

1.什么是JWT?#

JSON Web Token(JWT)是一種開放的標准(RFC 7519),JWT定義了一種緊湊且自包含的標准,該標准旨在將各個主體的信息包裝為 JSON 對象。主體信息是通過數字簽名進行加密和驗證的。常使用 HMAC 算法或 RSA(公鑰/私鑰的非對稱性加密)算法對JWT進行簽名,安全性很高。

JWT 特點:

  • 緊湊型:數據體積小,可通過 POST 請求參數或 HTTP 請求頭發送。
  • 自包含:JWT包含了主體的所有信息,避免了每個請求都需要向Uaa服務驗證身份,降低了服務器的負載。

2.JWT的結構#

JWT結構:

  • Header(頭)
  • Payload(有效載荷)
  • Signature(簽名)

因此,JWT的通常格式是:xxxxx.yyyyy.zzzzz

(1)Header

Header 通常是由兩部分組成:令牌的類型(即JWT)和使用的算法類型,如 HMAC、SHA256和RSA。例如:

Copy
{
    "typ": "JWT", "alg": "HS256" }

將 Header 用 Base64 編碼作為 JWT 的第一部分。

(2)Payload

這是 JWT 的第二部分,包含了用戶的一些信息和Claim(聲明、權利)。有3類型的 Claim:保留、公開和私人。

Copy
{
    "sub": "123456789", "name": "John Doe", "admin": true }

將 Payload 用 Base64 編碼作為 JWT 的第一部分。

(3)Signature

要創建簽名部分,需要將 Base64 編碼后的 Header、Payload 和秘鑰進行簽名,一個典型的格式如下:

Copy
HMACSHA256(
    base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret )

3.如何使用JWT#

認證流程圖如下,客戶端獲取JWT后,以后每次請求都不需要再通過Uaa服務來判斷該請求的用戶以及該用戶的權限。在微服務中,可以利用JWT實現單點登錄。

4.案例工程架構#

三個工程:

  • eureka-server:注冊服務中心,端口8761。這里不再演示搭建。
  • auth-service:負責授權,授權需要用戶提供客戶端的 clientId 和 password,以及授權用戶的username和password。這些信息准備無誤之后,auth-service 返回JWT,該 JWT 包含了用戶的基本信息和權限點信息,並通過 RSA 加密。
  • user-service:作為資源服務,它的資源以及被保護起來了,需要相應的權限才能訪問。user-service 服務得到用戶請求的 JWT 后,先通過公鑰解密JWT,得到該JWT對應的用戶的信息和用戶的權限信息,再判斷該用戶是否有權限訪問該資源。

工程架構圖:

5.構建auth-service工程#

1.新建Spring Boot工程,取名為 auth-service,其完整pom.xml文件為.

Copy
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>auth-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>auth-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-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.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--防止jks文件被mavne編譯導致不可用--> <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> </project>

2.配置application.yml文件

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

3.配置Spring Security

Copy
@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() //關閉CSRF .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()); //密碼加密 } }

UserServiceDetail.java

Copy
@Service public class UserServiceDetail implements UserDetailsService { @Autowired private UserDao userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } }

UserDao.java

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

User對象和上一篇文章的內容一樣,需要實現UserDetails接口,Role對象需要實現GrantedAuthority接口.

Copy
@Entity public class User implements UserDetails, Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column private String password; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private List<Role> authorities; public User() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } public void setAuthorities(List<Role> authorities) { this.authorities = authorities; } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
Copy
@Entity public class Role implements GrantedAuthority { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getAuthority() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } }

4.配置 Authorization Server

在 OAuth2Config 這個類中配置 AuthorizationServer,其代碼如下:

Copy
@Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() //將客戶端的信息存儲在內存中 .withClient("user-service") //創建了一個Client為"user-service"的客戶端 .secret("123456") .scopes("service") //客戶端的域 .authorizedGrantTypes("refresh_token", "password") //配置類驗證類型為 refresh_token和password .accessTokenValiditySeconds(12*300); //5min過期 } @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() { //注意此處需要相應的jks文件 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("fzp-jwt.jks"), "fzp123".toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(keyStoreKeyFactory.getKeyPair("fzp-jwt")); return converter; } }

5.生成 jks 文件

配置 JwtTokenStore 時需要使用 jks 文件作為 Token 加密的秘鑰。

jks 文件需要Java keytool工具,保證Java環境變量沒問題,打開計算機終端,輸入命令:

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

解釋,-alias 選項為別名,-keypass 和 -storepass 為密碼選項,-validity 為配置jks文件過期時間(單位:天)。

獲取的 jks 文件作為私鑰,只允許 Uaa 服務持有,並用作加密 JWT。也就是把生成的 jks 文件放到 auth-service 工程的resource目錄下。那么 user-service 這樣的資源服務,是如何解密 JWT 的呢?這時就需要使用 jks 文件的公鑰。獲取 jks 文件的公鑰命令如下:

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

這個命令要求你的計算機上安裝了openSSL(下載地址),然后手動把安裝的openssl.exe所在目錄配置到環境變量。

輸入密碼fzp123后,顯示的信息很多,我們只提取 PUBLIC KEY,即如下所示:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlCFiWbZXIb5kwEaHjW+/
7J4b+KzXZffRl5RJ9rAMgfRXHqGG8RM2Dlf95JwTXzerY6igUq7FVgFjnPbexVt3
vKKyjdy2gBuOaXqaYJEZSfuKCNN/WbOF8e7ny4fLMFilbhpzoqkSHiR+nAHLkYct
OnOKMPK1SwmvkNMn3aTEJHhxGh1RlWbMAAQ+QLI2D7zCzQ7Uh3F+Kw0pd2gBYd8W
+DKTn1Tprugdykirr6u0p66yK5f1T9O+LEaJa8FjtLF66siBdGRaNYMExNi21lJk
i5dD3ViVBIVKi9ZaTsK9Sxa3dOX1aE5Zd5A9cPsBIZ12spYgemfj6DjOw6lk7jkG
9QIDAQAB
-----END PUBLIC KEY-----

新建一個 public.cert 文件,將上面的公鑰信息復制到 public.cert 文件中並保存。並將文件放到 user-service 等資源服務的resources目錄下。到目前為止,Uaa 服務已經搭建完畢。

需要注意的是,Maven 在項目編譯時,可能會將 jks 文件編譯,導致 jks 文件亂碼,最后不可用。需要在工程的 pom 文件中添加以下內容:

Copy
<!--防止jks文件被mavne編譯導致不可用--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <nonFilteredFileExtensions> <nonFilteredFileExtension>cert</nonFilteredFileExtension> <nonFilteredFileExtension>jks</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin>

最后,別忘了在啟動類注解@EnableEurekaClient開啟服務注冊.

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

6.構建user-service資源服務#

1.新建Spring Boot工程,取名為user-service,其完整pom.xml文件:

Copy
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>user-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>user-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-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-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

2.配置文件application.yml

在工程的配置文件application.yml中,配置程序名為 user-service,端口號為 9090,另外,需要配置 feign.hystrix.enable 為true,即開啟 Feign 的 Hystrix 功能。完整的配置代碼如下:

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

3.配置Resource Server

在配置Resource Server之前,需要注入 JwtTokenStore 類型的 Bean。

Copy
@Configuration public class JwtConfig { @Autowired JwtAccessTokenConverter jwtAccessTokenConverter; @Bean @Qualifier("tokenStore") public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter); } @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() { //用作 JWT 轉換器 JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); Resource resource = new ClassPathResource("public.cert"); String publicKey ; try { publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); } catch (IOException e) { throw new RuntimeException(e); } converter.setVerifierKey(publicKey); //設置公鑰 return converter; } }

然后配置 Resource Server

Copy
@Configuration @EnableResourceServer //開啟Resource Server功能 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.新建一個配置類 GlobalMethodSecurityConfig,在此類中通過 @EnableGlobalMethodSecurity(prePostEnabled = true)注解開啟方法級別的安全驗證。

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

5.編寫用戶注冊接口

拷貝auth-service工程的User.java、Role.java 和 UserDao.java 到本工程。

在 Service 層的 UserService 寫一個插入用戶的方法,代碼如下

Copy
@Service public class UserServiceDetail { @Autowired private UserDao userRepository; public User insertUser(String username,String password){ User user=new User(); user.setUsername(username); user.setPassword(BPwdEncoderUtil.BCryptPassword(password)); return userRepository.save(user); } }

BPwdEncoderUtil工具類

Copy
public class BPwdEncoderUtil { private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); public static String BCryptPassword(String password){ return encoder.encode(password); } public static boolean matches(CharSequence rawPassword, String encodedPassword){ return encoder.matches(rawPassword,encodedPassword); } }

在 Web 層,在 Controller 中寫一個注冊的 API 接口 “/user/register”,代碼如下

Copy
@RestController @RequestMapping("/user") public class UserController { @Autowired UserServiceDetail userServiceDetail; @PostMapping("/register") public User postUser(@RequestParam("username") String username , @RequestParam("password") String password){ //參數判斷,省略 return userServiceDetail.insertUser(username,password); } }

6.編寫用戶登錄接口

在Service層,在 UserServiceDetail 中添加一個 login(登錄)方法,代碼如下:

Copy
@Service public class UserServiceDetail { @Autowired private AuthServiceClient client; public UserLoginDTO login(String username, String password){ User user=userRepository.findByUsername(username); if (null == user) { throw new UserLoginException("error username"); } if(!BPwdEncoderUtil.matches(password,user.getPassword())){ throw new UserLoginException("error password"); } // 獲取token JWT jwt=client.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 通過向 auth-service 服務遠程調用“/oauth/token” API接口,獲取 JWT。在 "/oauth/token" API 接口,獲取JWT。在“/oauth/token”API接口中需要在請求頭傳入 Authorization 信息,並需要傳請求參數認證類型 grant_type、用戶名 username 和密碼 password,代碼如下:

Copy
@FeignClient(value = "auth-service",fallback =AuthServiceHystrix.class ) public interface AuthServiceClient { @PostMapping(value = "/oauth/token") JWT getToken(@RequestHeader(value = "Authorization") String authorization, @RequestParam("grant_type") String type, @RequestParam("username") String username, @RequestParam("password") String password); }

其中,AuthServiceHystrix 為AuthServiceClient 的熔斷器,代碼如下:

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

JWT 為一個 JavaBean,它包含了 access_token、token_type 和 refresh_token 等信息,代碼如下:

Copy
public class JWT { private String access_token; private String token_type; private String refresh_token; private int expires_in; private String scope; private String jti; //getter setter

UserLoginDTO 包含了一個 User 和一個 JWT 對象,用於返回數據的實體:

Copy
public class UserLoginDTO { private JWT jwt; private User user; //setter getter }

登錄異常類 UserLoginException

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

統一異常處理

Copy
@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”.

Copy
@PostMapping("/login") public UserLoginDTO login(@RequestParam("username") String username , @RequestParam("password") String password){ //參數判斷,省略 return userServiceDetail.login(username,password); }

為了測試權限,再補充一個"/foo"接口,該接口需要“ROLE_ADMIN”權限.

Copy
@RequestMapping(value = "/foo", method = RequestMethod.GET) @PreAuthorize("hasAuthority('ROLE_ADMIN')") public String getFoo() { return "i'm foo, " + UUID.randomUUID().toString(); }

最后,在啟動類注解開啟Feign:

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

7.啟動工程,開始測試#

經過千辛萬苦,終於搭建了一個Demo工程,現在開始依次啟動 eureka-server、auth-service 和 user-service工程。這里我們使用PostMan測試編寫的接口。

1.注冊用戶

2.登錄獲取Token

3.訪問/user/foo

復制 access_token到 Header頭部,發起GET請求。

Copy
"Authorization":"Bearer {access_token}"

因為沒有權限,訪問被拒絕,我們手動在數據庫添加"ROLE_ADMIN"權限,並與該用戶關聯。重新登錄並獲取Token,重新請求“/user/foo”接口

總結#

在本案例中,用戶通過登錄接口來獲取授權服務的Token 。用戶獲取Token 成功后,在以后每次訪問資源服務的請求中都需要攜帶該Token 。資源服務通過公鑰解密Token ,解密成功后可以獲取用戶信息和權限信息,從而判斷該Token 所對應的用戶是誰, 具有什么權限。
這個架構的優點在於,一次獲取Token , 多次使用,不再每次詢問Uaa 服務該Token 所對應的用戶信息和用戶的權限信息。這個架構也有缺點,例如一旦用戶的權限發生了改變, 該Token 中存儲的權限信息並沒有改變, 需要重新登錄獲取新的Token 。就算重新獲取了Token,如果原來的Token 沒有過期,仍然是可以使用的,所以需要根據具體的業務場景來設置Token的過期時間。一種改進方式是將登錄成功后獲取的Token 緩存在網關上,如果用戶的權限更改,將網關上緩存的Token 刪除。當請求經過網關,判斷請求的Token 在緩存中是否存在,如果緩存中不存在該Token ,則提示用戶重新登錄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM