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搭建认证授权中心、资源服务中心、并结合网关校验的完整详细示例相关推荐

  1. Spring Security OAuth2.0认证授权知识概括

    Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...

  2. Spring Security OAuth2.0认证授权三:使用JWT令牌

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] 前面两篇文章详细讲解了如何基 ...

  3. Spring Security OAuth2.0认证授权五:用户信息扩展到jwt

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...

  4. Spring Security OAuth2.0认证授权

    文章目录 1.基本概念 1.1.什么是认证 1.2 什么是会话 1.3什么是授权 1.4授权的数据模型 1.4 RBAC 1.4.1 基于角色的访问控制 2.基于Session的认证方式 3.整合案例 ...

  5. 【Spring】Spring Security OAuth2 JWT 认证

    1.概述 Spring Security OAuth2 JWT 认证服务器配置 Spring Security OAuth2 JWT 资源服务器配置 Spring Security OAuth2 Re ...

  6. Spring Security OAuth2分布式系统认证解决方案

    目录 1 什么是分布式系统 2 分布式认证需求 3 分布式认证方案 3.1 选型分析 3.2 技术方案 4 OAuth2.0 4.1 OAuth2.0介绍 4.2 Spring Cloud Secur ...

  7. Spring Security+Oauth2四种授权模式

    上一篇文章:Spring Security + OAuth2.0项目搭建:https://blog.csdn.net/qq_42402854/article/details/123057625 接着认 ...

  8. SpringCloud 基于OAth2.0 搭建认证授权中心_02

    文章目录 一.数据库部分 1. 创建数据库 2. 初始化数据脚本 二.搭建maven父工程认证授权模块 2.1. 创建一个maven项目 2.2. 引入依赖 三.搭建认证授权模块 3.1. 创建一个子 ...

  9. Spring Security oAuth2创建认证服务器模块

    POM <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:// ...

  10. 基于Session的认证方式_实现授权功能_Spring Security OAuth2.0认证授权---springcloud工作笔记118

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 我们来实现基本的,session的授权功能,很简单实际上就是利用了springmvc的拦截器.不多 ...

最新文章

  1. 经典的”服务器最多65536个连接”误解
  2. (转载)文件系统与数据库系统的区别
  3. Java并发编程——线程池的使用
  4. 【UOJ34】—多项式乘法(FFT)
  5. Linux基础书籍推荐
  6. Tornado web 框架
  7. 容器rocker_用Rocker制作模板
  8. lincode 题目记录6
  9. win7 64位下自行编译OpenCV2.4.10+CUDA toolkit 5.5的整个过程以及需要注意的问题(opencv+cuda并行计算元素的使用)...
  10. Linux运行shell脚本出现出错,可能是环境编码不一致造成的。
  11. python取文件后缀
  12. 【转】脉络清晰的BP神经网络讲解,赞
  13. java mockserver搭建_使用Moco搭建Mock Server教程
  14. n维球体,n维椭球体体积公式证明
  15. Kinect for Windows SDK v2.0 开发笔记 (七)语音识别(上)
  16. #年轻人找工作应该把钱放第一位吗#
  17. cocoscreator 使用内置自带的资源和生成单色图片
  18. Mysql高级命令与概念
  19. 程序员外包到底怎么了?
  20. .net对Excel表数据读写操作

热门文章

  1. 计算机ip如何设置,win7电脑ip地址怎么设置_win7电脑ip怎么设置-win7之家
  2. js 调用后台方法 ajax,JS直接调用C#后台方法(ajax调用)
  3. 禁用/开启 Windows系统3D加速
  4. 百度地图室内路线规划
  5. nginx的带宽限制和并发控制
  6. 使用MQTTlens和Mosquitto在WIN7上完成发布和订阅
  7. win7下安装MQTT Paho客户端
  8. TP5 生成二维码并合成图片
  9. html计时加速,HTML-加速、再加速(下)_html
  10. 移动体验大作战,冰桶算法全盘点