网关请求拦截-(CheckJwtFilter.java)
package com.xiaoge.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoge.constant.GatewayConstant;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Classname CheckJwtFilter
* @Date 2022/3/20 下午4:49
* @Created by xiaoge
* @Description TODO
*/
@Configuration
public class CheckJwtFilter implements GlobalFilter, Ordered {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 过滤请求, 判断是否有jwt, 有放行, 没拦截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 获取路径
String path = request.getURI().getPath();
// 判断该路径是否在放行路径中
if (GatewayConstant.ALLOW_PATH.contains(path)) {
return chain.filter(exchange);
}
// 判断该请求头中是否包含Authorization
HttpHeaders headers = request.getHeaders();
List<String> list = headers.get(GatewayConstant.AUTHORIZATION);
if (!CollectionUtils.isEmpty(list)) {
String token = list.get(0);
if (StringUtils.isNotBlank(token)) {
String jwt = token.replace("bearer ", "");
if (StringUtils.isNotBlank(jwt)) {
// 看redis是否还有该token
Boolean hasKey = redisTemplate.hasKey(GatewayConstant.OAUTH_PREFIX + jwt);
if(hasKey) {
return chain.filter(exchange);
}
}
}
}
// 这里就是没有 jwt 了,返回 401
ServerHttpResponse response = exchange.getResponse();
// 设置响应头
response.getHeaders().add("content-type", "application/json;charset=utf-8");
Map<String, Object> map = new HashMap<>();
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg", "非法访问!");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = null;
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer buffer = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(buffer));
}
/**
* 执行顺序 越小越先 要比-1 小
* @return
*/
@Override
public int getOrder() {
return -2;
}
}
网关配置把获取到的token存入redis-(GatewayConfig.java)它是把我们访问网关的路由转发到授权微服务
package com.xiaoge.config;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.xiaoge.constant.GatewayConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
/**
* @Classname GatewayConfig
* @Date 2022/3/20 下午5:18
* @Created by xiaoge
* @Description TODO
*/
@Configuration
public class GatewayConfig {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 描述: 给授权专门做的存入 token 路由
*
* @param builder:
* @return org.springframework.cloud.gateway.route.RouteLocator */
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder .routes()
.route("auth-server-router", r -> r.path("/oauth/**")
.filters(f -> f.modifyResponseBody(String.class, String.class, (exchanges, s) -> {
String path = exchanges.getRequest().getURI().getPath();
if ("/oauth/token".equals(path)) {
//如果是登录的请求,那么得到的 s 就是 token 的一套
JSONObject jsonObject = JSONUtil.parseObj(s);
// 判断jsonObject 是否包含access_token
if (jsonObject.containsKey("access_token")) {
//如果包含 access_token 就放进 redis 里面
// token值
String access_token = jsonObject.getStr("access_token");
// 过期时间
Long expires_in = jsonObject.getLong("expires_in");
// 存入redis中
redisTemplate.opsForValue().set(GatewayConstant.OAUTH_PREFIX + access_token, "", Duration.ofSeconds(expires_in));
}
}
return Mono.just(s);
})).uri("lb://auth-server"))
.build();
}
}
授权服务认证配置-(AuthorizationConfig.java)
package com.xiaoge.config;
import com.xiaoge.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.security.KeyPair;
/**
* @Author: ZhangXiao
* @DateTime: 2022/4/7 15:59
* @Description:
* 1. token的存储
* 2. jwt的转换器
* 3. 第三方应用
* 4. endpoints暴露
*/
@Configuration
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsServiceImpl userDetailsService;
/**
* 使用 jwt 存放 token
* @return
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 使用非对称加密的方式
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
// 把私钥读到内存中
ClassPathResource resource = new ClassPathResource("cxs-jwt.jks");
// 创建一个钥匙工厂
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,"cxs123".toCharArray());
// 获取钥匙, 传入钥匙别名
KeyPair privateKey = keyStoreKeyFactory.getKeyPair("cxs-jwt");
// 设置进转换器里面
jwtAccessTokenConverter.setKeyPair(privateKey);
return jwtAccessTokenConverter;
}
/**
* 配置第三方应用
* password 只要是登录都用这个授权方式
* 客户端授权 用于微服务之间自发的进行远程调用时 资源服务器必须要token的情况, 当然也是可以放行服务提供者的接口的
*
*
* 描述: 一个 web 平台,用于第三方访问
* 一个 sxt 平台,内部访问的,例如 mq 里发起远程调用
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() // 配置第三方引用
.withClient("web") // 浏览器
.secret(passwordEncoder.encode("web-secret")) // 密码加密方式
.scopes("all") // 作用域
.authorizedGrantTypes("password") // 授权类型 密码授权
.accessTokenValiditySeconds(7200) // token的时间7200秒
.redirectUris("https://www.baidu.com") // token过期从定向地址
.and() // 上面是密码授权方式, 下面是客户端授权
.withClient("client") // 微服务之间自发的调用
.secret(passwordEncoder.encode("client-secret")) // 密码加密方式
.scopes("read") // 只读 业务方面的一个配置
.authorizedGrantTypes("client_credentials") // 授权类型 客户端授权
.accessTokenValiditySeconds(Integer.MAX_VALUE) // token过期时间 66年
.redirectUris("https://www.baidu.com"); // token过期从定向地址
}
/**
* 暴露出去
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.userDetailsService(userDetailsService) // 暴露密码登录实现类
.authenticationManager(authenticationManager) // 暴露密码登录需要的认证管理器
.tokenStore(tokenStore()) // 暴露tokenStore
.accessTokenConverter(jwtAccessTokenConverter()); // 暴露jwt转换器
super.configure(endpoints);
}
}
授权服务安全配置-(WebSecurityConfig.java)
package com.xiaoge.config;
import com.xiaoge.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @Author: ZhangXiao
* @DateTime: 2022/4/7 15:56
* @Description:
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
/**
* 认证管理器 密码登录需要认证管理器
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
/**
* 走自己的登录 密码登录需要的
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//走自己的登录
auth.userDetailsService(userDetailsService);
}
}
授权服务登录-(UserDetailsServiceImpl.java)
package com.xiaoge.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaoge.constant.AuthConstant;
import com.xiaoge.domain.SysUser;
import com.xiaoge.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @Author: ZhangXiao
* @DateTime: 2022/4/7 16:10
* @Description:
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserMapper sysUserMapper;
/**
* 登录方法
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 获取request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
// 获取请求头信息
String loginType = request.getHeader(AuthConstant.LOGIN_TYPE);
if (StringUtils.isEmpty(loginType)) {
return null;
}
// 选择
switch (loginType) {
case AuthConstant.SYS_USER:
// 后台用户 就查后台的sys_user表
SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(
SysUser::getUsername, username
));
if (!ObjectUtils.isEmpty(sysUser)) {
// 查询权限
List<String> auths = sysUserMapper.findUserAuthsById(sysUser.getUserId());
if (!CollectionUtils.isEmpty(auths)) {
// 设置权限
sysUser.setAuths(auths);
}
}
return sysUser;
case AuthConstant.MEMBER:
// 前台用户
return null;
default:
return null;
}
}
}
授权服务启动类-(AuthServerApplication.class) (一定要加这个认证注解EnableAuthorizationServer, 因为WebSecurityConfig配置了认证管理器)
package com.xiaoge;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
/**
* @Author: ZhangXiao
* @DateTime: 2022/4/7 15:36
* @Description:
*/
@SpringBootApplication
@EnableAuthorizationServer // 开启授权服务器
@EnableEurekaClient // 开启eureka客户端
@MapperScan(basePackages = "com.xiaoge.mapper")
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
/**
* 密码加密器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
postman-Authorization(web web-secret grant_type(认证方式)是AuthorizationConfig类中配置的)


源码地址:https://gitee.com/xiaogectwbq/ego-shop-cloud
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po