Spring Cloud Gateway 整合Spring Security
做了一个Spring Cloud项目,网关采用 Spring Cloud Gateway,想要用 Spring Security 进行权限校验,由于 Spring Cloud Gateway 采用 webflux ,所以平时用的 mvc 配置是无效的,本文实现了 webflu 下的登陆校验。
1. Security配置
这里先贴出配置类,方便了解大致情况。
其中涉及到的三个处理器均为自定义
package com.shop.jz.gateway.security.config;import com.shop.jz.gateway.security.constants.Constants;
import com.shop.jz.gateway.security.handler.AuthenticationFailureHandler;
import com.shop.jz.gateway.security.handler.AuthenticationSuccessHandler;
import com.shop.jz.gateway.security.handler.ShopHttpBasicServerAuthenticationEntryPoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;/*** @author:JZ* @date:2020/5/21*/
@Slf4j
@EnableWebFluxSecurity // 开启WebFluxSecurity,必须要添加
public class SecurityConfig {private String permitUrls = "/gateway/login1,/test1";/*** 鉴权成功处理器*/@Autowiredprivate AuthenticationSuccessHandler authenticationSuccessHandler;/*** 登陆验证失败处理器*/@Autowiredprivate AuthenticationFailureHandler authenticationFailureHandler;/*** 未登录访问资源时的处理类,若无此处理类,前端页面会弹出登录窗口*/@Autowiredprivate ShopHttpBasicServerAuthenticationEntryPoint shopHttpBasicServerAuthenticationEntryPoint;@Beanpublic SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {log.info("不进行权限校验url:{}", this.permitUrls);httpSecurity.authorizeExchange().pathMatchers(this.permitUrls.split(",")).permitAll().anyExchange().authenticated().and().httpBasic().and().formLogin().loginPage(Constants.LOGIN_URL) // 登陆地址.authenticationSuccessHandler(authenticationSuccessHandler) // 设置鉴权成功处理器.authenticationFailureHandler(authenticationFailureHandler) // 设置登陆验证失败处理器.and().exceptionHandling().authenticationEntryPoint(shopHttpBasicServerAuthenticationEntryPoint).and().csrf().disable() // 必须支持跨域.logout().logoutUrl(Constants.LOGOUT_URL); // 退出登陆地址return httpSecurity.build();}// 密码加密方式@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}}
2. 自定义 UserDetails
在Security中用户信息需存放在 UserDetails
中,UserDetails
是一个接口,可以使用Security已经实现的 org.springframework.security.core.userdetails.User
,也可以实现 UserDetails
接口自定义用户信息类。
package com.shop.jz.gateway.security.model;import com.jz.shop.user.dto.UserDto;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.Set;/*** @author:JZ* @date:2020/5/17*/
@Data
public class LoginUser implements UserDetails {/*** token*/private String token;/*** login time*/private Long loginTime;/*** expire time*/private Long expireTime;/*** Login IP address*/private String ip;/*** location*/private String location;/*** Browser type*/private String browser;/*** operating system*/private String os;/*** 用户名*/private String userName;/*** 账号密码*/private String userPwd;/*** 权限列表*/private Set<String> permissions;public LoginUser() {}public LoginUser(String userName, String userPwd, Set<String> permissions) {this.userName = userName;this.userPwd = userPwd;this.permissions = permissions;}public LoginUser getLoginUser() {return this;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return this.userPwd;}@Overridepublic String getUsername() {return this.userName;}/*** Whether the account has not expired, which cannot be verified*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** Specifies whether the user is unlocked. Locked users cannot authenticate*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** Indicates whether the user's credentials (passwords) have expired, which prevents authentication*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** Available, disabled users cannot authenticate*/@Overridepublic boolean isEnabled() {return true;}
}
3. 自定义获取用户信息
在 WebFlux 中Security通过调用 ReactiveUserDetailsService
接口的实现类获取用户信息,与 MVC 中的 UserDetailsService
不同。
package com.shop.jz.gateway.security.service;import com.jz.shop.commons.execptions.BaseException;
import com.jz.shop.user.api.UserApi;
import com.jz.shop.user.dto.UserDto;
import com.shop.jz.gateway.security.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;@Slf4j
@Service
public class ShopUserDetailsService implements ReactiveUserDetailsService {@Autowiredprivate UserApi userApi; // 自定义实现的用户信息查询的feign接口@Overridepublic Mono<UserDetails> findByUsername(String username) {try {UserDto user = this.userApi.getUserInfoByUsername(username);LoginUser loginUser = new LoginUser(user.getUserName(), user.getPassword(), null);return Mono.just(loginUser);} catch (BaseException baseException) {log.warn(baseException.getMsg());}return Mono.error(new UsernameNotFoundException("User Not Found"));}
}
4. 鉴权成功处理器
当用户名和密码通过校验后会进入 WebFilterChainServerAuthenticationSuccessHandler
,我们可以重写 onAuthenticationSuccess
方法实现自定义返回信息
package com.shop.jz.gateway.security.handler;import com.alibaba.fastjson.JSON;
import com.jz.shop.commons.model.Result;
import com.shop.jz.gateway.security.constants.Constants;
import com.shop.jz.gateway.security.model.LoginUser;
import com.shop.jz.gateway.security.service.TokenService;
import com.shop.jz.gateway.security.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.WebFilterChainServerAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.io.UnsupportedEncodingException;/*** 鉴权成功处理器* @author:JZ* @date:2020/5/21*/
@Slf4j
@Component
public class AuthenticationSuccessHandler extends WebFilterChainServerAuthenticationSuccessHandler {@Autowiredprivate TokenService tokenService;public AuthenticationSuccessHandler() {}@Overridepublic Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {ServerWebExchange exchange = webFilterExchange.getExchange();ServerHttpResponse response = exchange.getResponse();log.info("用户:{} 登陆成功");// 设置返回信息HttpHeaders headers = response.getHeaders();headers.add("Content-Type", "application/json; charset=UTF-8");String responseJson = JSON.toJSONString(Result.success());DataBuffer dataBuffer = null;try {dataBuffer = response.bufferFactory().wrap(responseJson.getBytes("UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}return response.writeWith(Mono.just(dataBuffer));}}
5. 登陆验证失败处理器
当账号密码或权限验证异常时,会进入该处理器。
package com.shop.jz.gateway.security.handler;import com.alibaba.fastjson.JSON;
import com.jz.shop.commons.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.io.UnsupportedEncodingException;/*** @author:JZ* @date:2020/5/21*/
@Slf4j
@Component
public class AuthenticationFailureHandler implements ServerAuthenticationFailureHandler {@Overridepublic Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException e) {log.warn("鉴权失败");ServerWebExchange exchange = webFilterExchange.getExchange();ServerHttpResponse response = exchange.getResponse();// 设置返回信息HttpHeaders headers = response.getHeaders();headers.add("Content-Type", "application/json; charset=UTF-8");String responseJson = JSON.toJSONString(Result.fail("鉴权失败"));DataBuffer dataBuffer = null;try {dataBuffer = response.bufferFactory().wrap(responseJson.getBytes("UTF-8"));} catch (UnsupportedEncodingException ex) {ex.printStackTrace();}return response.writeWith(Mono.just(dataBuffer));}}
6. 未登录访问资源时的处理器
package com.shop.jz.gateway.security.handler;import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;/*** @author:JZ* @date:2020/5/21*/
@Slf4j
@Component
public class ShopHttpBasicServerAuthenticationEntryPoint extends HttpBasicServerAuthenticationEntryPoint {private static final String WWW_AUTHENTICATE = "WWW-Authenticate";private static final String DEFAULT_REALM = "Realm";private static String WWW_AUTHENTICATE_FORMAT = "Basic realm=\"%s\"";private String headerValue = createHeaderValue("Realm");public ShopHttpBasicServerAuthenticationEntryPoint() {}public void setRealm(String realm) {this.headerValue = createHeaderValue(realm);}private static String createHeaderValue(String realm) {Assert.notNull(realm, "realm cannot be null");return String.format(WWW_AUTHENTICATE_FORMAT, new Object[]{realm});}@Overridepublic Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add("Content-Type", "application/json; charset=UTF-8");response.getHeaders().set(HttpHeaders.AUTHORIZATION, this.headerValue);JSONObject result = new JSONObject();result.put("code", "000000");result.put("msg", "未登录鉴权");DataBuffer dataBuffer = response.bufferFactory().wrap(result.toJSONString().getBytes());return response.writeWith(Mono.just(dataBuffer));}}
Spring Cloud Gateway 整合Spring Security相关推荐
- 实战干货!Spring Cloud Gateway 整合 OAuth2.0 实现分布式统一认证授权!
今天这篇文章介绍一下Spring Cloud Gateway整合OAuth2.0实现认证授权,涉及到的知识点有点多,有不清楚的可以看下陈某的往期文章. 文章目录如下: 微服务认证方案 微服务认证方案目 ...
- Spring Cloud Gateway 整合 knife4j 聚合接口文档
当系统中微服务数量越来越多时,如果任由这些服务散落在各处,那么最终管理每个项目的接口文档将是一件十分麻烦的事情,单是记住所有微服务的接口文档访问地址就是一件苦差事了.当如果能够将所有微服务项目的接口文 ...
- Spring Cloud Gateway 整合阿里 Sentinel网关限流实战!
前一篇文章介绍了Spring Cloud Gateway的一些基础知识点,今天陈某就来唠一唠网关层面如何做限流? 文章目录如下: 网关如何限流? Spring Cloud Gateway本身自带的限流 ...
- Spring Cloud Gateway 整合阿里 Sentinel网关限流实战
文章目录如下: 网关如何限流? Spring Cloud Gateway本身自带的限流实现,过滤器是RequestRateLimiterGatewayFilterFactory,不过这种上不了台面的就 ...
- spring cloud gateway 与spring cloud版本对应问题
spring cloud gateway 与spring cloud版本对应问题 问题描述 学习B站谷粒商场,因为项目package版本总是不对应,冒出各种问题,今天学到网关内容,又出现版本对应问题. ...
- Spring Cloud Gateway(二):Spring Cloud Gateway整合Eureka应用
Spring Cloud Gateway 应用概述 下面的示例启动两个服务:gataway-server 和 user-service 都注册到注册中心 Eureka上,客户端请求后端服务[user- ...
- Spring Cloud Gateway整合Nacos实现服务路由及集群负载均衡
目录 一.序言 二.代码示例 1.父工程spring-cloud-gateway-learning 2.子工程spring-cloud-api-gateway (1) pom.xml (2) 配置文件 ...
- 一. spring cloud gateway集成 spring cloud stream binder kafka,实现“动态路由“刷新与加载之采坑记录
一.前言 Spring Cloud Stream是用于构建消息驱动的微服务应用程序的框架. 本文主要介绍如何集成 Spring Cloud Stream,以 Kafka发布订阅模式(topic),实现 ...
- 微服务网关spring cloud gateway入门详解
1.API网关 API 网关是一个处于应用程序或服务( REST API 接口服务)之前的系统,用来管理授权.访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的 ...
最新文章
- Python分析离散心率信号(上)
- 用Windows系统实现RAID功能
- matlab自动重合闸,电力系统自动重合闸matlab仿真 - 图文
- 人工智能抢80万工人的饭碗,真的会失业吗?但是有新的转机
- linux java调用so文件路径_Linux平台Java调用so库-JNI使用例子
- 云计算的下半场:云原生
- Git的基础知识和常用命令
- bat命令 修改ini文件内容_关于mysql使用中文乱码;mysql修改ini文件无法启动;以及mysql卸载无法安装的一系列问题说明。...
- python几多级证书_Openssl 生成多级证书
- UWP开发细节记录:判断文件类型
- Linux的守护进程
- 红黑树简介与C++应用
- flexsim怎么设置传送带方向_Flexsim仿真教程学习(六)-分拣系统
- 【uni-app】uni-app移动端开发 - 登录界面
- 算法—青蛙跳台阶问题汇总
- 关于手机上的卫星定位
- android个人开发者广告平台
- python读取数据集csv文件_读取CSV文件并使用python提取所需的数据量
- 做自媒体视频剪辑为什么赚不到钱?
- 使用Android Studio开发一个简易的音乐播放器