#user nobody;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 32 解决此错误需要增加下两行配置
server_names_hash_max_size 2048;# 【值为域名长度总和】;
server_names_hash_bucket_size 2048;# 【上升值】
# /sockjs-node 访问异常->
# 参考:https://blog.csdn.net/qq27229639/article/details/103069055
# https://www.ancii.com/anbgjpemb
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name www.authorization.life;
rewrite ^(.*)$ https://$server_name$1 permanent;
}
# HTTPS server 参考配置: https://nenufm.com/archives/g
server {
listen 443 ssl;
server_name www.authorization.life;
ssl_certificate D://authorization_life_aliyun//authorization_life_chain.pem;
ssl_certificate_key D://authorization_life_aliyun//authorization_life_key.key;
# ssl验证相关配置
ssl_protocols TLSv1.3 SSLv3; #安全链接可选的加密协议
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1; #为ECDHE密码指定 SEPO384Q1
ssl_session_timeout 10m; #缓存有效期
ssl_session_cache shared:SSL:10m;
ssl_prefer_server_ciphers on;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
#后端服务gateway
location / {
proxy_pass http://127.0.0.1:9000;
}
# 前端登录工程
location /login {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}


<!--
由于 Spring Authorization Server 需要 Java 8 或更高版本的运行时环境,此次将直接使用 jdk17
查看版本依赖关系:
spring-cloud 官网: https://spring.io/projects/spring-cloud
spring-cloud-alibaba 官网: https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
springboot 官网: https://spring.io/projects/spring-boot
2022年9月4日
得出结果:
boot:2.7.3
cloud:2021.0.3
cloud-alibaba:2.2.8.RELEASE
spring-security:5.7.3
authorization-server:0.3.1
-->
<!--基本依赖-->
<spring-boot.version>2.7.3</spring-boot.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version>
<jackson-bom.version>2.13.3</jackson-bom.version>
<!--权限认证依赖-->
<spring-security.version>5.7.3</spring-security.version>
<nimbus-jose-jwt.version>9.23</nimbus-jose-jwt.version>
<oauth2-oidc-sdk.version>9.27</oauth2-oidc-sdk.version>
<oauth2-authorization-server.version>0.3.1</oauth2-authorization-server.version>
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>${oauth2-oidc-sdk.version}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose-jwt.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>${oauth2-authorization-server.version}</version>
</dependency>
<!--以上为security+oauth2-->
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson-bom.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-bom</artifactId>
<version>${spring-security.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--以上为基础的导入依赖-->
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
OAuth2TokenCustomizer<JwtEncodingContext> oAuth2TokenCustomizer) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer<>();
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
// 配置请求拦截
http.requestMatcher(endpointsMatcher)
.authorizeRequests(authorizeRequests -> authorizeRequests
// // 无需认证即可访问
.antMatchers(SecurityConstant.IGNORE_PERM_URLS).permitAll()
//除以上的请求之外,都需要token
.anyRequest().authenticated())
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
//配置formLogin
.formLogin(Customizer.withDefaults())
//将oauth2.0的配置托管给 SpringSecurity
.apply(authorizationServerConfigurer);
// 设置accesstoken为jwt形式
http.setSharedObject(OAuth2TokenCustomizer.class, oAuth2TokenCustomizer);
// 配置 异常处理
http
.exceptionHandling()
//当未登录的情况下 该如何跳转。
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint());
return http.build();
}
/**
* 根据数据库中的client信息转换
*
* @param clientId clientId
* @param oauthClient 数据库client
* @return RegisteredClient
*/
private RegisteredClient getRegisteredClient(String clientId, OauthClient oauthClient) {
RegisteredClient.Builder builder = RegisteredClient.withId(clientId)
.clientId(oauthClient.getClientId())
.clientSecret(oauthClient.getClientSecret())
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.redirectUri(oauthClient.getRedirectUri())
// JWT的配置项 包括TTL 是否复用refreshToken等等
.clientSettings(ClientSettings.builder()
//是否需要用户确认一下客户端需要获取用户的哪些权限
.requireAuthorizationConsent(false)
.build())
.tokenSettings(TokenSettings.builder()
//配置使用自定义的jwtToken格式化,配置此处才会使用到 CustomizerOAuth2Token , 或者不配置此格式化的配置,将默认生成jwt的形式
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
//是否可重用刷新令牌
.reuseRefreshTokens(true)
//accessToken 的有效期 单位:秒
.accessTokenTimeToLive(Duration.of(oauthClient.getAccessTokenTimeout(), ChronoUnit.SECONDS))
//refreshToken 的有效期 单位:秒
.refreshTokenTimeToLive(Duration.of(oauthClient.getRefreshTokenTimeout(), ChronoUnit.SECONDS))
.build());
//批量设置当前的授权类型
Arrays.stream(oauthClient.getGrantTypes().split(StrPool.COMMA))
.map(grantType -> {
if (CharSequenceUtil.equals(grantType, AuthorizationGrantType.AUTHORIZATION_CODE.getValue())) {
return AuthorizationGrantType.AUTHORIZATION_CODE;
} else if (CharSequenceUtil.equals(grantType, AuthorizationGrantType.REFRESH_TOKEN.getValue())) {
return AuthorizationGrantType.REFRESH_TOKEN;
} else if (CharSequenceUtil.equals(grantType, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())) {
return AuthorizationGrantType.CLIENT_CREDENTIALS;
} else if (CharSequenceUtil.equals(grantType, AuthorizationGrantType.PASSWORD.getValue())) {
return AuthorizationGrantType.PASSWORD;
} else if (CharSequenceUtil.equals(grantType, AuthorizationGrantType.JWT_BEARER.getValue())) {
return AuthorizationGrantType.JWT_BEARER;
} else {
throw new RegClientException("不支持的授权模式, [" + grantType + "]");
}
}).forEach(builder::authorizationGrantType);
Arrays.stream(oauthClient.getScopes().split(StrPool.COMMA))
.forEach(builder::scope);
return builder.build();
}
/**
* 注册client
*
* @param clientService 自定义的client端信息
* @return RegisteredClientRepository
*/
@Bean
public RegisteredClientRepository registeredClientRepository(OauthClientService clientService) {
return new RegisteredClientService(clientService);
}
在此处将不再赘述。可以在文章结尾查看源码。
对于access_token,需要哪些信息,将掌控到什么程度,在这里具体的体现,由开发人员指定,为系统中更敏捷的开发做准备。
/**
* JWT的加密算法,说明:https://www.rfc-editor.org/rfc/rfc7515
*
* @return JWKSource
*/
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = Jwks.generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
/**
* 将重写jwtToken的中的信息,并将其存储到redis中。
*
* @param context JwtEncodingContext
*/
@Override
public void customize(JwtEncodingContext context) {
//此处的token字符串是前端拿到的jwtToken信息中解密后的字符串,在这里将自定义jwtToken的实现,将定制jwt的 header 和 claims,将此token存放到 claim 中
String token = UUID.randomUUID().toString(true);
Authentication principal = context.getPrincipal();
Authentication authorizationGrant = context.getAuthorizationGrant();
OAuth2Authorization authorization = context.getAuthorization();
Set<String> authorizedScopes = context.getAuthorizedScopes();
ProviderContext providerContext = context.getProviderContext();
RegisteredClient registeredClient = context.getRegisteredClient();
log.info("principal-{}", JSONUtil.toJsonStr(principal));
log.info("authorization-{}", JSONUtil.toJsonStr(authorization));
log.info("authorizedScopes-{}", JSONUtil.toJsonStr(authorizedScopes));
log.info("authorizationGrant-{}", JSONUtil.toJsonStr(authorizationGrant));
log.info("providerContext-{}", JSONUtil.toJsonStr(providerContext));
log.info("registeredClient-{}", JSONUtil.toJsonStr(registeredClient));
UserDetail userDetail = null;
// 目的是为了定制jwt 的header 和 claims
if (principal instanceof OAuth2ClientAuthenticationToken) {
//如果当前登录的是client,则进行封装client
// userDetail = securityAuthUserService.createUserDetailByClientId(registeredClient.getClientId());
}
// else if (principal.getPrincipal() instanceof UserDetail) {
// //如果当前登录的是系统用户,则进行封装userDetail
// userDetail = securityAuthUserService.createUserDetailByUser((UserDetails) principal.getPrincipal());
// }
else if (principal.getPrincipal() instanceof User) {
//如果当前登录的是系统用户,则进行封装userDetail
userDetail = securityAuthUserService.createUserDetailByUser((User) principal.getPrincipal());
}
//如果解析失败,则抛出异常信息。
if (Objects.isNull(userDetail)) {
log.error("在自定义token实现中, 用户信息解析异常。");
userDetail = new UserDetail();
}
//也需要将此token存放到当前登录用户中,为了在退出登录时进行获取redis中的信息并将其删除
userDetail.setToken(token);
//将用户信息放置到redis中,并设置其过期时间为 client中的过期时间
strRedisHelper.strSet(LifeSecurityConstants.getUserTokenKey(token), userDetail,
registeredClient.getTokenSettings().getAccessTokenTimeToLive().getSeconds(), TimeUnit.SECONDS);
log.info("生成的用户-token是-{},此token作为key,用户信息作为value存储到redis中", token);
//也可以在此处将当前登录用户的信息存放到jwt中,但是这样就不再安全。
context.getClaims().claim(LifeSecurityConstants.TOKEN, token).build();
}
重写 logout 方法,对 当前登录用户中的token进行删除,并返回 退出登录成功提示。
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
log.info("进入退出登录处理器。");
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
try {
UserDetail userDetail = UserHelper.getUserDetail();
log.debug("当前登录用户-UserDetail-是:" + userDetail);
if (Objects.nonNull(userDetail)) {
String userToken = userDetail.getToken();
log.debug("当前登录用户的token-是:" + userToken);
String cacheUserToken = KvpFormat.of(SecurityConstant.USER_DETAIL).add("token", userToken).format();
redisHelper.delete(cacheUserToken);
redisHelper.delete(KvpFormat.of(SecurityConstant.TOKEN_STORE).add("userId", userDetail.getUserId().toString()).format());
}
SecurityContextHolder.clearContext();
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
log.debug("请求头-Authorization-是:" + token);
if (StrUtil.isBlank(token)) {
PrintWriter out = response.getWriter();
out.write(JSONUtil.toJsonStr(new Res<>(Res.ERROR, "未找到token,请确认已登录。", null)));
out.flush();
out.close();
}
token = token.split(" ")[1];
OAuth2Authorization auth2Authorization = oAuth2AuthorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
log.debug("查询出来-OAuth2Authorization-是:" + JSONUtil.toJsonStr(auth2Authorization));
if (Objects.nonNull(auth2Authorization)) {
oAuth2AuthorizationService.remove(auth2Authorization);
}
PrintWriter out = response.getWriter();
out.write(JSONUtil.toJsonStr(new Res<>(Res.SUCCESS, Res.SUCCESS_DESC, null)));
out.flush();
out.close();
} catch (IOException e) {
log.error("退出登录处理器处理失败,", e);
}
}
此处的配置将指定了 springsecurity中的自定义登录页面,对于 RouterFunction 可自行网上查询。
对于前后端分离中如何指定登录页面,如何跳转到登录页面,这个有很长一段时间由于知识的不足而不知如何处理,此配置将解决此问题,nginx中配置 / 代理 gwteway网关工程, 当访问根目录时将重定向到 /login 路径上,nginx中对 /login 路径做代理到 前端工程中, 其 /login 所指向的前端工程就是 自定义的登录页面。
@Slf4j
@Configuration
public class RouterFunctionConfig {
/**
* 访问根目录时将重定向到登录模块
*
* @return 登录模块
*/
@Bean
public RouterFunction<ServerResponse> loginRouterFunction() {
return RouterFunctions.route(
RequestPredicates.GET("/"),
request -> ServerResponse.temporaryRedirect(URI.create(request.uri() + "login")).build());
}
}
将解析 /oauth/token 接口中返回的accessToken信息,获取其中的 token字段对应的redis存储的用户信息,并验证是否能解析,在转换为用户信息的之后,将对请求路径做校验,判断是否拥有此权限。
此处是gateway中做的accessToken解析,但是路由后的服务可能是不知道的,这个时候,我们需要对此用户信息再次做一层转换,转换为Jwt形式的token,在公共的core模块中对此token进行解析,并放置到 SecurityContextHolder.getContext()中。
// JwtTokenGatewayFilterFactory
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String token = getToken(request);
// 获取当前用户的信息
String userDetailStr = StrUtil.isBlank(token) ? null : redisHelper.strGet(LifeSecurityConstants.getUserTokenKey(token));
// 若jwt不存在,则封入一个空字符串,到权限拦截器处理。因为有些api是不需要登录的,故在此不处理。
UserDetail userDetail = StrUtil.isNotBlank(userDetailStr) ? JsonHelper.readValue(userDetailStr, UserDetail.class) : null;
userDetailStr = Optional.ofNullable(userDetailStr).orElse(StrUtil.EMPTY);
// 创建JWS对象
JWSObject jwsObject = new JWSObject(jwsHeader, new Payload(userDetailStr));
// 签名并序列化转换为真正存储用户信息的jwtToken
String jwtToken = Jwts.signAndSerialize(jwsObject, signer);
ServerWebExchange jwtExchange = exchange.mutate()
.request(request.mutate()
.header(Jwts.HEADER_JWT, jwtToken).build())
.build();
return chain.filter(jwtExchange)
.contextWrite(ctx -> ctx.put(RequestContext.CTX_KEY,
ctx.<RequestContext>getOrEmpty(RequestContext.CTX_KEY)
.orElse(new RequestContext())
.setUserDetail(userDetail)));
};
}
// JwtAuthenticationFilter
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
log.info("authentication-{}",JSONUtil.toJsonStr(authentication));
log.info("请求路径是-{}", JSONUtil.toJsonStr(request.getRequestURI()));
String jwt = request.getHeader(Jwts.HEADER_JWT);
log.info("进入到-JwtAuthenticationFilter-过滤器-jwtToken-{}", jwt);
if (StrUtil.isBlank(jwt)) {
chain.doFilter(request, response);
return;
}
JWSObject jwsObject = Jwts.parse(jwt);
if (!Jwts.verify(jwsObject, verifier)) {
log.error("Jwt verify failed! JWT: [{}]", jwt);
chain.doFilter(request, response);
return;
}
UserDetail userDetail = jwsObject.getPayload().toType(payload -> StrUtil.isBlank(payload.toString()) ?
UserDetail.anonymous() : JsonHelper.readValue(payload.toString(), UserDetail.class));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetail, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
解决参考:
https://blog.csdn.net/qq27229639/article/details/103069055
https://www.ancii.com/anbgjpemb
需要放置到一个 单独的 @Configuration 配置类中,不然会不生效的。

我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev
我有一个放在lib/network中的类:moduleNetworkApiclassNetworkProxyendend然后在另一个类中,我引用了这个类:network_proxy=::NetworkApi::NetworkProxy.new(params)一切都在我的开发环境中正常运行,但是当我部署到服务器时,我在上面一行收到错误消息:NameError:uninitializedconstantNetworkApi::NetworkProxy我不知道为什么会出现这个奇怪的错误。请告诉我为什么。 最佳答案 请注意Rails5dis
我有一个Rails2.3.5应用程序,其中包含我希望保护的API。没有用户-它是一个应用到应用风格的网络服务(更像是亚马逊服务而不是facebook),所以我想使用两条腿的OAuth方法来实现它。我一直在尝试使用oauth-plugin服务器实现作为开始:http://github.com/pelle/oauth-plugin...但它的构建需要三足(网络重定向流)oauth。在我深入研究对其进行更改以支持两条腿之前,我想看看是否有更简单的方法,或者是否有人有更好的方法让Rails应用程序实现成为两条腿的OAuth提供程序。 最佳答案
我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li
我想在随机字符串前后添加一个空格。我试过使用"Random_string".center(1,"")但它不起作用。谢谢 最佳答案 我发现这是最优雅的解决方案:padded_string="#{random_string}"走简单的路没有错。 关于ruby-在ruby中的字符串前后添加空格?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/3357897/
目录SpringBootStarter是什么?以前传统的做法使用SpringBootStarter之后starter的理念:starter的实现: 创建SpringBootStarter步骤在idea新建一个starter项目、直接执行下一步即可生成项目。 在xml中加入如下配置文件:创建proterties类来保存配置信息创建业务类:创建AutoConfiguration测试如下:SpringBootStarter是什么? SpringBootStarter是在SpringBoot组件中被提出来的一种概念、简化了很多烦琐的配置、通过引入各种SpringBootStarter包可以快速搭建出一
我想用一个(自己的)omniauth提供商来衡量每秒可以登录多少次。我需要了解此omniauth/oauth请求的性能如何,以及此身份验证是否具有可扩展性?到目前为止我得到了什么:defperformance_auth(user_count=10)bm=Benchmark.realtimedouser_count.timesdo|n|forkdoclick_on'Logout'omniauth_config_mock(:provider=>"foo",:uid=>n,:email=>"foo#{n}@example.net")visit"/account/auth/foo/"enden
我想在服务器启动时在我的Rails应用程序中订阅一个mqtt主题,并保持订阅始终处于事件状态和运行状态。我正在使用这个mqttgem进行mqtt通信:https://github.com/njh/ruby-mqtt这是我现在拥有的:在application.rb中:config.after_initializedomqttSub=BackgroundMQTT.newmqttSub.runend后台MQTT类:classMQTTSubscriberdefrunThread.newdoMQTT::Client.connect(:host=>'localhost',:port=>1883,)
我正在尝试为使用omniauth-google-oauth2gem创建session编写测试。我是否需要将env["omniauth.auth"]变量与post:create一起传递?也许当我试图这样做时,我做错了。我得到的错误如下所示...Rake测试错误1)Error:SessionsControllerTest#test_should_get_create:NoMethodError:undefinedmethod`provider'fornil:NilClassapp/models/user.rb:6:in`from_omniauth'app/controllers/sessi