javascript
Spring Security OAuth2搭建认证授权中心、资源服务中心、并结合网关校验的完整详细示例
Spring Security OAuth2搭建認證授權中心、資源服務中心、并結合網關校驗
- 認證授權中心
- 添加依賴
- application.yml配置
- Security配置
- 登錄認證配置
- Oauth2參數配置類
- 授權服務配置
- 執行測試
- 增強令牌
- 重構端點
- 重構令牌
- 執行測試
- 資源服務中心
- 認證異常配置
- 創建資源服務
- 提供資源
- 執行測試
- 網關校驗
- 添加依賴
- 配置application.yml
- 路徑白名單配置類
- 網關過濾器
- 執行測試
)
認證授權中心
添加依賴
<!-- spring web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- spring data redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><!-- hutool --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- spring cloud security --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId></dependency><!-- spring cloud oauth2 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependencyManagement><dependencies><!-- spring cloud 依賴 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR8</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>application.yml配置
server:port: 8888 # 端口spring:application:name: oauth2-server # 應用名# 數據庫datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456url: jdbc:mysql://127.0.0.1:3306/demo?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false# Redisredis:port: 6379host: 127.0.0.1timeout: 3000database: 1password:# Oauth2 client:oauth2:client-id: appId # 客戶端標識 IDsecret: 123456 # 客戶端安全碼# 授權類型grant_types:- password- refresh_token# token 有效時間,單位秒token-validity-time: 2592000refresh-token-validity-time: 2592000# 客戶端訪問范圍scopes:- api- all# Mybatis mybatis:configuration:map-underscore-to-camel-case: true # 開啟駝峰映射Security配置
配置使用Redis存儲Token信息 配置密碼的加密、解密、校驗邏輯 初始化認證管理對象 配置請求訪問的放行和認證規則 import cn.hutool.crypto.digest.DigestUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import javax.annotation.Resource;/*** Security配置*/ @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter {/*** 注入Redis連接工廠*/@Resourceprivate RedisConnectionFactory redisConnectionFactory;/*** 初始化RedisTokenStore,用于將token存儲至Redis** @return*/@Beanpublic RedisTokenStore redisTokenStore() {RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);// 設置key的層級前綴redisTokenStore.setPrefix("TOKEN:");return redisTokenStore;}/*** 初始化密碼編碼器,指定編碼與校驗規則,用MD5加密密碼** @return*/@Beanpublic PasswordEncoder passwordEncoder() {// Security官方推薦的BCryptPasswordEncoder加密與校驗類// 密鑰的迭代次數(默認為10)//return new BCryptPasswordEncoder(10);return new PasswordEncoder() {/*** 加密* @param rawPassword 原始密碼* @return*/@Overridepublic String encode(CharSequence rawPassword) {return DigestUtil.md5Hex(rawPassword.toString());}/*** 校驗密碼* @param rawPassword 原始密碼* @param encodedPassword 加密密碼* @return*/@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {return DigestUtil.md5Hex(rawPassword.toString()).equals(encodedPassword);}};}/*** 初始化認證管理對象** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 放行和認證規則** @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 禁用csrfhttp.csrf().disable().authorizeRequests()// 放行的請求.antMatchers("/oauth/**", "/actuator/**").permitAll().and().authorizeRequests()// 其他請求必須認證才能訪問.anyRequest().authenticated();} }登錄認證配置
創建UserService類實現UserDetailsService類重寫loadUserByUsername方法,該方法主要實現登錄、認證校驗邏輯,這里簡單模擬。
public interface UserMapper {/*** 根據用戶名 or 手機號 or 郵箱查詢用戶信息* @param account* @return*/@Select("select id, username, phone, email, password, roles from user where " +"(username = #{account} or phone = #{account} or email = #{account})")Diners selectByAccountInfo(@Param("account") String account);} @Service public class UserService implements UserDetailsService {@Resourceprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {if (StrUtil.hasBlank(username)) {throw new RuntimeException("用戶名不可為空");}User user= userMapper.selectByAccountInfo(username);if (user == null) {throw new UsernameNotFoundException("用戶名或密碼錯誤,請重新輸入");}return new User(username, user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(diners.getRoles()));}}Oauth2參數配置類
讀取application.yaml文件中的Oauth2配置信息,并封裝到ClientOAuth2DataConfiguration類
package com.example.demo.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;/*** 客戶端配置類*/ @Component @ConfigurationProperties(prefix = "client.oauth2") @Data public class ClientOAuth2DataConfiguration {/*** 客戶端標識ID*/private String clientId;/*** 客戶端安全碼*/private String secret;/*** 授權類型*/private String[] grantTypes;/*** token有效期*/private int tokenValidityTime;/*** refresh-token有效期*/private int refreshTokenValidityTime;/*** 客戶端訪問范圍*/private String[] scopes; }授權服務配置
package com.example.demo.config;import com.example.demo.service.UserService; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import javax.annotation.Resource;/*** 授權服務配置*/ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {/*** RedisTokenSore*/@Resourceprivate RedisTokenStore redisTokenStore;/*** 認證管理對象*/@Resourceprivate AuthenticationManager authenticationManager;/*** 密碼編碼器*/@Resourceprivate PasswordEncoder passwordEncoder;/*** 客戶端配置類*/@Resourceprivate ClientOAuth2DataConfiguration clientOAuth2DataConfiguration;/*** 登錄校驗*/@Resourceprivate UserService userService;/*** 配置令牌端點安全約束** @param security* @throws Exception*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {// 允許訪問token的公鑰,默認/oauth/token_key是受保護的security.tokenKeyAccess("permitAll()")// 允許檢查token的狀態,默認/oauth/check_token是受保護的.checkTokenAccess("permitAll()");}/*** 客戶端配置 - 授權模型** @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory().withClient(clientOAuth2DataConfiguration.getClientId()) // 客戶端標識 ID.secret(passwordEncoder.encode(clientOAuth2DataConfiguration.getSecret())) // 客戶端安全碼.authorizedGrantTypes(clientOAuth2DataConfiguration.getGrantTypes()) // 授權類型.accessTokenValiditySeconds(clientOAuth2DataConfiguration.getTokenValidityTime()) // token 有效期.refreshTokenValiditySeconds(clientOAuth2DataConfiguration.getRefreshTokenValidityTime()) // 刷新 token 的有效期.scopes(clientOAuth2DataConfiguration.getScopes()); // 客戶端訪問范圍}/*** 配置授權以及令牌的訪問端點和令牌服務** @param endpoints* @throws Exception*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 認證器endpoints.authenticationManager(authenticationManager)// 具體登錄的方法.userDetailsService(userService)// token 存儲的方式:Redis.tokenStore(redisTokenStore);}}執行測試
請求 localhost:8888/oauth/token
參數設置
執行請求
查看Redis
增強令牌
增強令牌就是豐富、自定義令牌包含的信息,這部分信息是客戶端能直接看到的
重構端點
重構/oauth/token端點
/*** Oauth2控制器*/ @RestController @RequestMapping("oauth") public class OAuthController {@Resourceprivate TokenEndpoint tokenEndpoint;@Resourceprivate HttpServletRequest request;/*** 自定義Token返回對象** @param principal* @param parameters* @return* @throws HttpRequestMethodNotSupportedException*/@PostMapping("token")public HashMap<String, Object> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {OAuth2AccessToken auth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) auth2AccessToken;Map<String, Object> data = new LinkedHashMap(token.getAdditionalInformation());data.put("accessToken", token.getValue());data.put("expireIn", token.getExpiresIn());data.put("scopes", token.getScope());if (token.getRefreshToken() != null) {data.put("refreshToken", token.getRefreshToken().getValue());}data.put("path", request.getServletPath());return BaseUtil.back(1, data);} }執行測試
重構令牌
創建SignInIdentity登錄認證對象類實現UserDetails
package com.example.demo.model;import cn.hutool.core.util.StrUtil; import lombok.Getter; import lombok.Setter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream;/*** 登錄認證對象*/ @Getter @Setter public class SignInIdentity implements UserDetails {/*** 主鍵*/private Integer id;/*** 用戶名*/private String username;/*** 昵稱*/private String nickname;/*** 密碼*/private String password;/*** 手機號*/private String phone;/*** 郵箱*/private String email;/*** 頭像*/private String avatarUrl;/*** 角色*/private String roles;/*** 是否有效 0=無效 1=有效*/private int isValid;/*** 角色集合, 不能為空*/private List<GrantedAuthority> authorities;/*** 獲取角色信息** @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if (StrUtil.isNotBlank(this.roles)) {String[] strings = this.roles.split(",");// 獲取數據庫中的角色信息this.authorities = Stream.of(strings).map(role -> {return new SimpleGrantedAuthority(role);}).collect(Collectors.toList());} else {// 如果角色為空則設置為ROLE_USERthis.authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");}return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return this.isValid != 0;}}修改登錄認證
@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {if (StrUtil.hasBlank(username)) {throw new RuntimeException("用戶名不可為空");}Diners diners = dinersMapper.selectByAccountInfo(username);if (diners == null) {throw new UsernameNotFoundException("用戶名或密碼錯誤,請重新輸入");}// 初始化登錄認證對象SignInIdentity signInIdentity = new SignInIdentity();// 拷貝屬性BeanUtils.copyProperties(diners, signInIdentity);return signInIdentity;// return new User(username, diners.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(diners.getRoles()));}令牌增強
/*** 配置授權以及令牌的訪問端點和令牌服務** @param endpoints* @throws Exception*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 認證器endpoints.authenticationManager(authenticationManager)// 具體登錄的方法.userDetailsService(userService)// token 存儲的方式:Redis.tokenStore(redisTokenStore)// 令牌增強對象,增強返回的結果.tokenEnhancer((accessToken, authentication) -> {// 獲取登錄用戶的信息,然后設置SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();LinkedHashMap<String, Object> map = new LinkedHashMap<>();map.put("nickname", signInIdentity.getNickname());map.put("avatarUrl", signInIdentity.getAvatarUrl());DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;token.setAdditionalInformation(map);return token;});}執行測試
請求 localhost:8888/oauth/token
資源服務中心
登錄成功,得到token,通過token獲取資源
認證異常配置
創建MyAuthenticationEntryPoint類,處理認證失敗出現異常時的處理邏輯。
package com.example.demo.config;import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.ObjectMapper; import com.example.demo.utils.BaseUtil; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component;import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap;/*** 認證失敗處理*/ @Component public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {@Resourceprivate ObjectMapper objectMapper;/*** 認證失敗處理邏輯** @param request* @param response* @param authException* @throws IOException*/@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {// 返回 JSONresponse.setContentType("application/json;charset=utf-8");// 狀態碼 401response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);// 寫出PrintWriter out = response.getWriter();String errorMessage = authException.getMessage();if (StrUtil.isBlank(errorMessage)) {errorMessage = "登錄失效!";}HashMap<String, Object> result = BaseUtil.back(0, errorMessage, errorMessage);// ResultInfo result = ResultInfoUtil.buildError(ApiConstant.ERROR_CODE, errorMessage, request.getRequestURI());out.write(objectMapper.writeValueAsString(result));out.flush();out.close();}}創建資源服務
package com.example.demo.config;import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;import javax.annotation.Resource;/*** 資源服務*/ @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Resourceprivate MyAuthenticationEntryPoint authenticationEntryPoint;/*** 配置放行的資源** @param http* @throws Exception*/@Overridepublic void configure(HttpSecurity http) throws Exception {//所有請求必須認證通過http.authorizeRequests()//其他地址需要認證授權;.anyRequest().authenticated().and()//下邊的路徑放行.requestMatchers().antMatchers("/user/**");}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.authenticationEntryPoint(authenticationEntryPoint);}}提供資源
package com.example.demo.controller;import com.example.demo.model.SignInIdentity; import com.example.demo.utils.BaseUtil; import io.micrometer.core.instrument.util.StringUtils; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.HashMap;/*** 用戶中心*/ @RestController public class UserController {@Resourceprivate RedisTokenStore redisTokenStore;/*** 獲取登錄用戶的信息** @param authentication* @return*/@GetMapping("user/getLoginUser")public HashMap<String, Object> getCurrentUser(Authentication authentication) {SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();HashMap<String, Object> map = new HashMap<>();map.put("username", signInIdentity.getUsername());map.put("phone", signInIdentity.getPhone());map.put("email", signInIdentity.getEmail());return BaseUtil.back(1, "獲取資源成功", map);}/*** 安全退出** @param access_token* @param authorization* @return*/@GetMapping("user/logout")public HashMap<String, Object> logout(String access_token, String authorization) {// 判斷access_token是否為空,為空將authorization賦值給access_tokenif (StringUtils.isBlank(access_token)) {access_token = authorization;}// 判斷authorization是否為空if (StringUtils.isBlank(access_token)) {return BaseUtil.back(1, "退出成功");}// 判斷bearer token是否為空if (access_token.toLowerCase().contains("bearer ".toLowerCase())) {access_token = access_token.toLowerCase().replace("bearer ", "");}// 清除redis token信息OAuth2AccessToken oAuth2AccessToken = redisTokenStore.readAccessToken(access_token);if (oAuth2AccessToken != null) {redisTokenStore.removeAccessToken(oAuth2AccessToken);OAuth2RefreshToken refreshToken = oAuth2AccessToken.getRefreshToken();redisTokenStore.removeRefreshToken(refreshToken);}return BaseUtil.back(1, "退出成功");} }執行測試
請求localhost:8888/oauth/token獲取token
{"code": 1,"message": "Successful.", "data": {"nickname": "test","avatarUrl": "/test","accessToken": "2cf71a49-1f62-4e93-b27c-7cb0b4419ab4","expireIn": 2588653,"scopes": ["api"],"refreshToken": "154aefe0-a0fa-43d4-91fb-c70b1e2998e4","path": "/oauth/token"} }使用token獲取服務資源,有兩種方式:
方式一:
請求localhost:8888/user/getLoginUser?access_token=2cf71a49-1f62-4e93-b27c-7cb0b4419ab4獲取資源
{"msg": "獲取資源成功","code": 1,"data": {"phone": "13666666666","email": null,"username": "test"} }方式二:
請求localhost:8888/user/getLoginUser,使用Bearer auth認證
獲取資源
校驗token
請求 localhost:8888/oauth/check_token?token=2cf71a49-1f62-4e93-b27c-7cb0b4419ab4
校驗token成功時:
{"avatarUrl": "/test","user_name": "test","scope": ["api"],"nickname": "test","active": true,"exp": 1629734432,"authorities": ["ROLE_USER"],"client_id": "appId" }校驗token失敗時:
{"error": "invalid_token","error_description": "Token was not recognised" }安全退出
1.請求localhost:8888/user/logout?access_token=2cf71a49-1f62-4e93-b27c-7cb0b4419ab4
2.請求localhost:8888/user/logout,使用Bearer auth認證
{"msg": "退出成功","code": 1,"data": null }退出后再次請求資源
{"msg": "Invalid access token: 2cf71a49-1f62-4e93-b27c-7cb0b4419ab4","code": 0,"data": "Invalid access token: 2cf71a49-1f62-4e93-b27c-7cb0b4419ab4" }網關校驗
添加依賴
<!-- spring cloud gateway --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- eureka client --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>配置application.yml
server:port: 9999spring:application:name: gateway-servercloud:gateway:discovery:locator:enabled: true # 開啟配置注冊中心進行路由功能lower-case-service-id: true # 將服務名稱轉小寫routes:- id: oauth2-serveruri: lb://oauth2-serverpredicates:- Path=/auth/**filters:- StripPrefix=1# 自定義參數 secure:ignore:urls: # 配置白名單路徑- /actuator/**- /auth/oauth/**- /user/getLoginUser- /user/logout# 配置 Eureka Server 注冊中心 eureka:instance:prefer-ip-address: trueinstance-id: ${spring.cloud.client.ip-address}:${server.port}client:service-url:defaultZone: http://localhost:8080/eureka/路徑白名單配置類
/*** 網關白名單配置*/ @Data @Component @ConfigurationProperties(prefix = "secure.ignore") public class IgnoreUrlsConfig {private List<String> urls;}網關過濾器
package com.example.demo.gateway.filter;import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.example.demo.utils.BaseUtil; import com.example.demo.config.IgnoreUrlsConfig; import org.apache.commons.lang.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.client.RestTemplate; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;import javax.annotation.Resource; import java.nio.charset.StandardCharsets; import java.util.HashMap;/*** 網關全局過濾器*/ @Component public class AuthGlobalFilter implements GlobalFilter, Ordered {@Resourceprivate IgnoreUrlsConfig ignoreUrlsConfig;@Resourceprivate RestTemplate restTemplate;/*** 身份校驗處理** @param exchange* @param chain* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 判斷當前的請求是否在白名單中AntPathMatcher pathMatcher = new AntPathMatcher();boolean flag = false;String path = exchange.getRequest().getURI().getPath();for (String url : ignoreUrlsConfig.getUrls()) {if (pathMatcher.match(url, path)) {flag = true;break;}}// 白名單放行if (flag) {return chain.filter(exchange);}// 獲取 access_tokenString access_token = exchange.getRequest().getQueryParams().getFirst("access_token");// 判斷access_token是否為空if (StringUtils.isBlank(access_token)) {return this.writeError(exchange, "請登錄");}// 校驗token是否有效String checkTokenUrl = "http://oauth2-server/oauth/check_token?token=".concat(access_token);try {// 發送遠程請求,驗證 tokenResponseEntity<String> entity = restTemplate.getForEntity(checkTokenUrl, String.class);// token無效業務邏輯處理if (entity.getStatusCode() != HttpStatus.OK) {return this.writeError(exchange, "請求失敗");}if (StringUtils.isBlank(entity.getBody())) {return this.writeError(exchange, "獲取token失敗");}} catch (Exception e) {return this.writeError(exchange, "token校驗失敗");}// 放行return chain.filter(exchange);}/*** 網關過濾器的排序,數字越小優先級越高** @return*/@Overridepublic int getOrder() {return 0;}@Resourceprivate ObjectMapper objectMapper;public Mono<Void> writeError(ServerWebExchange exchange, String error) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.OK);response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);HashMap<String, Object> back = BaseUtil.back(0, error);String resultInfoJson;DataBuffer buffer = null;try {resultInfoJson = objectMapper.writeValueAsString(back);buffer = response.bufferFactory().wrap(resultInfoJson.getBytes(StandardCharsets.UTF_8));} catch (JsonProcessingException ex) {ex.printStackTrace();}return response.writeWith(Mono.just(buffer));}}執行測試
請求localhost:9999/auth/oauth/token獲取token
{"code": 1,"message": "Successful.", "data": {"nickname": "test","avatarUrl": "/test","accessToken": "7a3d7102-39eb-4d02-be4c-9a705c9db616","expireIn": 2591999,"scopes": ["api"],"refreshToken": "6c974314-a13c-473a-8cdc-fb8373b3cce5","path": "/oauth/token"} }請求localhost:9999/auth/user/getLoginUser?access_token=7a3d7102-39eb-4d02-be4c-9a705c9db616獲取服務資源
{"msg": "獲取資源成功","code": 1,"data": {"phone": "13666666666","email": null,"username": "test"} }請求localhost:9999/auth/user/logout?access_token=7a3d7102-39eb-4d02-be4c-9a705c9db616安全退出
{"msg": "退出成功","code": 1,"data": null }總結
以上是生活随笔為你收集整理的Spring Security OAuth2搭建认证授权中心、资源服务中心、并结合网关校验的完整详细示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VoIP技术基础
- 下一篇: HDU - 3966 树链刨分