欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 前端技术 > javascript >内容正文

javascript

Spring Security OAuth2搭建认证授权中心、资源服务中心、并结合网关校验的完整详细示例

发布时间:2023/12/18 javascript 43 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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认证


获取资源

{"msg": "获取资源成功","code": 1,"data": {"phone": "13666666666","email": null,"username": "test"} }

校验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搭建认证授权中心、资源服务中心、并结合网关校验的完整详细示例的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。