使用Spring Security和OAuth 2.0保护Spring微服务架构

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

每个开发人员都希望能够更快,更有效地进行构建以支持规模。 使用Spring构建微服务架构可以为您的架构增加弹性和弹性,这将使其优雅地失效并无限扩展。

借助Spring Security及其OAuth 2.0支持,您还可以获得锁定API网关以及后端服务器所需的一切。 您可以将其设置为自动将访问令牌从一个应用程序传播到另一个应用程序,以确保在此过程中所有内容保持安全和加密。

本教程向您展示如何将Spring Security与OAuth 2.0和Okta结合使用来锁定您的微服务架构。

带有Spring Boot + Spring Cloud的微服务架构

本教程将向您展示如何为我之前写的教程“ 使用Spring Boot为Microbrews构建微服务体系结构”增加安全性。 带有Spring Boot和Spring Cloud的基本微服务架构如下图所示。

完成本教程后,您将获得Spring Security锁定的一切,Okta将为OAuth提供授权。 您的边缘服务(也称为API网关)将具有一个Feign客户端和一个处理正常故障转移的Hystrix,该客户端将随您的访问令牌一起传递。

首先,您需要克隆上述文章的已完成项目。

git clone https://github.com/oktadeveloper/spring-boot-microservices-example.git

在Okta中创建Web应用程序

如果您还没有,请创建一个永久免费的Okta Developer帐户 。 完成设置过程后,登录到您的帐户并导航至Applications > Add Application 。 单击Web然后单击下一步 。 在下一页上,输入以下值,然后单击完成

  • 应用名称: Spring OAuth
  • 基本URI: http://localhost:8081
  • 登录重定向URI: http://localhost:8081/login

记下clientId和client机密值,因为它们将用于配置Spring Boot应用程序。

您需要在ID令牌中添加一个roles声明,以便将Okta中的组转换为Spring Security机构。 在Okta开发人员控制台中,导航到API > 授权服务器 ,单击授权服务器选项卡并编辑默认选项卡。 点击索赔标签,然后添加索赔 。 将其命名为“角色”,并将其包含在ID令牌中。 将值类型设置为“ Groups”,并将过滤器设置为.*的正则表达式。

将Spring Security OAuth添加到边缘服务应用程序

边缘服务应用程序处理与beer-catalog-service的通信,因此它是开始集成OAuth的最佳位置。 在edge-service/pom.xml ,添加Spring Security的依赖关系,其OAuth支持和JWT支持。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>2.0.1.RELEASE</version>
</dependency>

将以下Zuul路由添加到edge-service/src/main/resources/application.properties

zuul.routes.beer-catalog-service.path=/beers
zuul.routes.beer-catalog-service.url=http://localhost:8080zuul.routes.home.path=/home
zuul.routes.home.url=http://localhost:8080

打开edge-service/src/main/java/com/example/edgeservice/EdgeServiceApplication.java并添加@EnableOAuth2Sso以启用OAuth身份验证。

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
...
@EnableOAuth2Sso
@SpringBootApplication
public class EdgeServiceApplication {

添加@EnableOAuth2Sso导致Spring Security查找大量属性。 将以下属性添加到edge-service/src/main/resources/application.properties

security.oauth2.client.client-id={yourClientId}
security.oauth2.client.client-secret={yourClientSecret}
security.oauth2.client.access-token-uri=https://{yourOktaDomain}.com/oauth2/default/v1/token
security.oauth2.client.user-authorization-uri=https://{yourOktaDomain}.com/oauth2/default/v1/authorize
security.oauth2.client.scope=openid profile email
security.oauth2.resource.user-info-uri=https://{yourOktaDomain}.com/oauth2/default/v1/userinfo
security.oauth2.resource.token-info-uri=https://{yourOktaDomain}.com/oauth2/default/v1/introspect
security.oauth2.resource.prefer-token-info=false

提示:如果在上面的代码片段中看到{yourOktaDomain} ,请登录到Okta帐户并刷新此页面。 它将用您的域替换该值。

ResourceServerConfig.java类添加到与EdgeServiceApplication相同的包中。

package com.example.edgeservice;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.web.util.matcher.RequestHeaderRequestMatcher;@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.requestMatcher(new RequestHeaderRequestMatcher("Authorization")).authorizeRequests().antMatchers("/**").authenticated();}
}

至此,您已经完成了足以登录到Edge Service应用程序的配置,但是它无法与下游beer-catalog-service进行通信。

将Spring Security OAuth添加到Beer Catalog Service

beer-catalog-service/pom.xml ,添加与添加到Edge Service相同的依赖项,以及Thymeleaf的依赖项。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>2.0.1.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

将相同的属性添加到beer-catalog-service/src/main/resources/application.properties

security.oauth2.client.client-id={yourClientId}
security.oauth2.client.client-secret={yourClientSecret}
security.oauth2.client.access-token-uri=https://{yourOktaDomain}.com/oauth2/default/v1/token
security.oauth2.client.user-authorization-uri=https://{yourOktaDomain}.com/oauth2/default/v1/authorize
security.oauth2.client.scope=openid profile email
security.oauth2.resource.user-info-uri=https://{yourOktaDomain}.com/oauth2/default/v1/userinfo
security.oauth2.resource.token-info-uri=https://{yourOktaDomain}.com/oauth2/default/v1/introspect
security.oauth2.resource.prefer-token-info=false

提示:添加这些属性的替代方法是使用环境变量。 例如, SECURITY_OAUTH2_CLIENT_CLIENT_ID将是用于指定security.oauth2.client.client-id的环境变量。 使用环境变量将允许您从一个位置更改两个应用程序的设置。

beer-catalog-service/src/main/java/com/example/beercatalogservice/HomeController.java创建一个HomeController来呈现用户信息,以便您可以验证身份验证是否正常。

package com.example.beercatalogservice;import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;import java.security.Principal;
import java.util.Map;@Controller
public class HomeController {@GetMapping("/home")@SuppressWarnings("unchecked")public String howdy(Model model, Principal principal) {OAuth2Authentication authentication = (OAuth2Authentication) principal;Map<String, Object> user = (Map<String, Object>) authentication.getUserAuthentication().getDetails();model.addAttribute("user", user);return "home";}
}

beer-catalog-service/src/main/resources/templates/home.html创建一个home.html模板,并使用以下代码填充它。

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head><style>th {text-align: left;}td {white-space: nowrap;}td:first-child {font-family: "Courier", monospace;font-size: 0.9em;color: #343434;}</style>
</head>
<body>
<h1>Hello<span th:if="${user}" th:text="' ' + ${user.name}"> Joe</span>!</h1>
<div th:unless="${user}"><a th:href="@{/login}">Login</a>
</div>
<div th:if="${user}"><form id="logoutForm" th:action="@{/logout}" method="post"><input type="submit" value="Logout"/></form>
</div><h2>User Properties</h2>
<table><thead><tr><th>Name</th><th>Value</th></tr></thead><tbody><tr><td>sub</td><td th:text="${user.sub}"></td></tr><tr><td>name</td><td th:text="${user.name}"></td></tr><tr><td>given_name</td><td th:text="${user.given_name}"></td></tr><tr><td>family_name</td><td th:text="${user.family_name}"></td></tr><tr><td>preferred_username</td><td th:text="${user.preferred_username}"></td></tr><tr><td>email</td><td th:text="${user.email}"></td></tr><tr><td>roles</td><td th:text="${user.roles}"></td></tr></tbody>
</table>
</body>
</html>

在与HomeController相同的包中创建ResourceServerConfig.java类。 此类配置Spring Security,因此可以保护所有端点,但那些通过Authorization标头访问的端点除外。

package com.example.beercatalogservice;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.web.util.matcher.RequestHeaderRequestMatcher;@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.requestMatcher(new RequestHeaderRequestMatcher("Authorization")).authorizeRequests().anyRequest().fullyAuthenticated();}
}

添加伪装的RequestInterceptor

用于与beer-catalog-service对话的@FeignClient Authorization标头。 为了使其UserFeignClientInterceptor ,请在与EdgeServiceApplication相同的目录中创建UserFeignClientInterceptor类。

package com.example.edgeservice;import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;@Component
public class UserFeignClientInterceptor implements RequestInterceptor {private static final String AUTHORIZATION_HEADER = "Authorization";private static final String BEARER_TOKEN_TYPE = "Bearer";@Overridepublic void apply(RequestTemplate template) {SecurityContext securityContext = SecurityContextHolder.getContext();Authentication authentication = securityContext.getAuthentication();if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, details.getTokenValue()));}}
}

注册为@Bean里面EdgeServiceApplication类。

import feign.RequestInterceptor;
...
public class EdgeServiceApplication {public static void main(String[] args) {SpringApplication.run(EdgeServiceApplication.class, args);}@Beanpublic RequestInterceptor getUserFeignClientInterceptor() {return new UserFeignClientInterceptor();}
}

为了使Hystrix了解安全上下文,您需要在edge-service/src/main/resources/application.properties 添加两个属性 :

feign.hystrix.enabled=true
hystrix.shareSecurityContext=true

验证安全通信

您可以通过启动所有Spring Boot应用程序来验证edge-servicebeer-catalog-service之间的通信。 首先,启动eureka-service

cd eureka-service
./mvnw spring-boot:run

在新的终端窗口中,启动beer-catalog-service

cd beer-catalog-service
./mvnw spring-boot:run

在另一个终端窗口中,启动edge-service

cd edge-service
./mvnw spring-boot:run

打开浏览器并导航到http://localhost:8081/good-beers 。 您应该被重定向到Okta域,并看到一个登录页面,提示您输入凭据。

输入您用来创建帐户的凭据,结果将看到一列优质啤酒。

如果您尝试导航到http://localhost:8081/home ,它将无法正常工作。 这是因为您需要将Spring Cloud Security添加到edge-service/pom.xml以中继Zuul代理的访问令牌。

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-security</artifactId>
</dependency>

没有这种依赖性,对/good-beers请求将可以工作(因为已配置Feign),但对/home请求将不会(因为Zuul需要Spring Cloud Security)。

重新启动边缘服务器应用程序,导航到http://localhost:8081/home ,您将在下一页看到您的用户详细信息。

在Spring Boot 2.0中保护下游服务

使用Spring Boot 1.5.x,将Actuator作为依赖项包括在内将触发Actuator Security并使其受到保护,从而保护了http://localhost:8080 。 在Spring Boot 2.x中,拥有WebSecurityConfigurerAdapter会导致执行器安全性降低。 在Beer Catalog Service应用程序中, ResourceServerConfig导致此行为。

要确保执行器端点安全并使其无法直接访问http://localhost:8080 ,请在beer-catalog-service/src/main/resources/application.properties添加要公开的端点:

management.endpoints.web.exposure.include=beans,mappings

然后创建一个SecurityConfig类(与ResourceServerConfig放在同一包中)。

package com.example.beercatalogservice;import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN").anyRequest().authenticated().and().httpBasic();}
}

进行这些更改后,重新启动beer-catalog-service并见证其保护。

注意:由于出现403错误,我无法使注销按钮正常工作。 我尝试在边缘服务应用程序csrf().requireCsrfProtectionMatcher(r -> false)ResourceServerConfig ,但这没有帮助。 我给Spring Security团队发送了一封电子邮件,询问他们是否有任何建议。

将Okta的登录小部件添加到Angular客户端

要使用Okta的登录小部件,您需要在Okta中修改您的应用以启用“ 隐式”授予类型。 登录到您的帐户,导航至“ 应用程序” >“ Spring OAuth” >“ 常规”选项卡,然后单击“ 编辑” 。 在“ 允许的授予类型”下启用“ 隐式(混合)” ,并选中其下方的两个复选框。 在登录重定向URI下添加http://localhost:4200 ,然后点击保存

为了使“登录小部件”向该应用程序发出请求,您还需要将客户端URL配置为可信来源。 单击API > 可信 来源 > 添加来源 。 输入http://localhost:4200作为原始URL,并选中其下方的两个复选框。

打开一个终端,导航到spring-boot-microservices-example/client ,然后使用npm安装客户端的依赖项。

cd client
npm install

安装Okta的登录小部件 ,使其可以与受保护的服务器进行通信。

npm install @okta/okta-signin-widget --save

将小部件CSS添加到client/src/styles.css

@import '~@okta/okta-signin-widget/dist/css/okta-sign-in.min.css';
@import '~@okta/okta-signin-widget/dist/css/okta-theme.css';

创建client/src/app/shared/okta/okta.service.ts并使用它来配置小部件以与您的Okta租户对话。 请确保在下面的代码中替换{yourOktaDomain}{clientId}

import { Injectable } from '@angular/core';
import * as OktaSignIn from '@okta/okta-signin-widget';@Injectable()
export class OktaService {widget;constructor() {this.widget = new OktaSignIn({baseUrl: 'https://{yourOktaDomain}.com',clientId: '{yourClientId}',authParams: {issuer: 'default',responseType: ['id_token', 'token'],scopes: ['openid', 'email', 'profile']}});}getWidget() {return this.widget;}getIdToken() {return this.widget.tokenManager.get('idToken');}getAccessToken() {return this.widget.tokenManager.get('accessToken');}
}

OktaService作为提供程序添加到client/src/app/app.module.ts

import { OktaService } from './shared/okta/okta.service';@NgModule({...providers: [OktaService],bootstrap: [AppComponent]
})
export class AppModule { }

修改client/src/app/shared/beer/beer.service.ts以读取访问令牌,并将其设置在Authorization标头中(如果存在)。

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { OktaService } from '../okta/okta.service';@Injectable()
export class BeerService {constructor(private http: HttpClient, private oktaService: OktaService) {}getAll(): Observable {let headers: HttpHeaders = new HttpHeaders();if (this.oktaService.getAccessToken()) {const accessToken = this.oktaService.getAccessToken();// headers is immutable, so re-assignheaders = headers.append('Authorization', accessToken.tokenType + ' ' + accessToken.accessToken);}return this.http.get('http://localhost:8081/good-beers', {headers: headers});}
}

修改app.component.html ,为小部件添加一个占位符,并显示一个部分,以显示用户名和注销按钮。

<mat-toolbar color="primary"><span>Welcome to {{title}}!</span>
</mat-toolbar><!-- Container to inject the Sign-In Widget -->
<div id="okta-signin-container"></div><div *ngIf="user"><h2>Welcome {{user?.name}}!</h2><button mat-raised-button (click)="logout()">Logout</button><app-beer-list></app-beer-list>
</div>

您会注意到HTML中的user变量。 要解决此问题,您需要更改client/src/app/app.component.ts以使其呈现登录小部件。 Angular的ChangeDetectorRef用于在事物发生更改并且渲染需要处理更新的变量时通知Angular。

import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { OktaService } from './shared/okta/okta.service';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {title = 'app';user;signIn;constructor(private oktaService: OktaService, private changeDetectorRef: ChangeDetectorRef) {this.signIn = oktaService.getWidget();}showLogin() {this.signIn.renderEl({el: '#okta-signin-container'}, (response) => {if (response.status === 'SUCCESS') {response.forEach(token => {if (token.idToken) {this.signIn.tokenManager.add('idToken', token);this.user = this.getUser(token);}if (token.accessToken) {this.signIn.tokenManager.add('accessToken', token);}});this.signIn.remove();this.changeDetectorRef.detectChanges();}});}getUser(token) {return {name: token.claims.name,email: token.claims.email,username: token.claims.preferred_username};}ngOnInit() {this.signIn.session.get((response) => {if (response.status !== 'INACTIVE') {const token = this.oktaService.getIdToken();this.user = this.getUser(token);this.changeDetectorRef.detectChanges();} else {this.showLogin();}});}logout() {this.signIn.signOut(() => {this.user = undefined;this.changeDetectorRef.detectChanges();this.showLogin();});}
}

为了使BeerListComponent (在src/app/beer-list/beer-list.component.ts )来检测你已经登录,您需要使用添加在构造函数依赖ChangeDetectorRef并调用它的detectChanges()方法时您可以在每种beer上设置giphyUrl属性。

import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { BeerService, GiphyService } from '../shared';@Component({selector: 'app-beer-list',templateUrl: './beer-list.component.html',styleUrls: ['./beer-list.component.css'],providers: [BeerService, GiphyService]
})
export class BeerListComponent implements OnInit {beers: Array<any>;constructor(private beerService: BeerService, private giphyService: GiphyService,private changeDetectorRef: ChangeDetectorRef) { }ngOnInit() {this.beerService.getAll().subscribe(data => {this.beers = data;for (const beer of this.beers) {this.giphyService.get(beer.name).subscribe(url => {beer.giphyUrl = url;this.changeDetectorRef.detectChanges();});}},error => console.log(error))}
}

验证身份验证作品

通过打开终端,导航到client目录,然后运行npm start client 。 将浏览器打开到http://localhost:4200 ,您应该看到类似以下的登录表单。

如果要调整表单的样式,以免它与顶部工具栏不对,请在styles.css添加以下内容。

#okta-signin-container {margin-top: 25px;

}
您应该能够登录,看到欢迎消息以及注销按钮。 但是,由于控制台中出现以下错误,您不会看到啤酒清单。

Failed to load http://localhost:8081/good-beers: Response for preflight is invalid (redirect)

发生这种情况是因为Spring Security无法识别/good-beers端点上的@CrossOrigin批注。 要解决此问题,请向EdgeServiceApplication添加一个simpleCorsFilter

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;import java.util.Collections;
...
public class EdgeServiceApplication {public static void main(String[] args) {SpringApplication.run(EdgeServiceApplication.class, args);}@Beanpublic RequestInterceptor getUserFeignClientInterceptor() {return new UserFeignClientInterceptor();}@Beanpublic FilterRegistrationBean simpleCorsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true);config.setAllowedOrigins(Collections.singletonList("*"));config.setAllowedMethods(Collections.singletonList("*"));config.setAllowedHeaders(Collections.singletonList("*"));source.registerCorsConfiguration("/**", config);FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));bean.setOrder(Ordered.HIGHEST_PRECEDENCE);return bean;}
}

重新启动边缘服务应用程序,然后重试。 这次您应该取得巨大的成功!

注意:如果在生产中使用此配置,则应将允许的来源从*更改为客户的URL。

部署到Cloud Foundry

要使用Pivotal Web Services在Cloud Foundry上部署所有内容,您需要创建一个帐户,下载/安装Cloud Foundry CLI并登录(使用cf login -a api.run.pivotal.io )。

部署所有服务和Angular客户端要进行生产涉及很多步骤。 因此,我编写了一个deploy.sh脚本来自动执行所有操作。

注意:该脚本完成后,您必须将客户端的URL作为登录重定向URI添加到Okta应用中。 您还需要在API > Trusted Origins下将其添加为

提示:如果收到错误消息,说明您使用的内存过多,则可能必须升级Cloud Foundry订阅。

进一步了解Spring Boot,OAuth 2.0和微服务

本文向您展示了如何使用Spring Security,OAuth和Okta保护微服务架构。 借助Zuul,Feign和Spring Cloud Security,您可以确保后端服务安全地通信。

本教程的源代码位于GitHub的“ oauth”分支中 。

git clone https://github.com/oktadeveloper/spring-boot-microservices-example.git
git checkout oauth

本教程向您展示了如何在上一教程“ 使用Spring Boot为Microbrews构建微服务架构 ”中增加安全性。

如果您有兴趣了解Spring Security和OAuth 2.0的未来,请参阅我们的Spring Security团队的好朋友Joe Grandja提供的有关Spring Security的下一代OAuth 2.0支持 。

此外,JHipster对其OAuth支持使用相同的设置。 如果您对将Okta与JHipster结合使用感兴趣,建议您阅读以下博客文章:

  • 使用OAuth 2.0和JHipster开发微服务架构
  • 使用Ionic for JHipster创建具有OIDC身份验证的移动应用程序

在developer.okta.com/product上了解有关Okta及其API的更多信息。 如果您对本教程有疑问,请在下面发表评论或在Twitter @mraible上打我。

变更日志:

  • 2018年5月11日:更新为使用Spring Boot 2.0和Okta登录小部件2.0.8。 请参阅spring-boot-microservices-example#17中的示例应用程序更改; 可以在okta.github.io#2049中查看对此帖子的更改。

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

使用Spring Security和OAuth 2.0保护Spring微服务体系结构最初于2018年2月13日发布在Okta开发者博客上。

翻译自: https://www.javacodegeeks.com/2018/05/secure-a-spring-microservices-architecture-with-spring-security-and-oauth-2-0.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一种使用setdll+HOOK钩子技术+dll为MFC程序ProtocalTool等老旧程序打补丁的思路(含源码)

一、引言 由于工作原因,需要使用一个很老旧的软件,没有源代码,该软件在XP系统下运行正常,但是需要登录,且在win10系统下使用时IP控件运行不正常,只能输入每个数字只能输入2位数,还有一些其他问题,比如给软件添加一些编辑框,或者对软件进行 下面简单梳理一下解决这些…

访存优化_Hibernate事实:多级访存

访存优化在多个级别上检索根实体及其子关联是很常见的。 在我们的示例中&#xff0c;我们需要使用其树&#xff0c;分支和叶子加载森林&#xff0c;并且我们将尝试查看Hibernate在三种集合类型上的表现&#xff1a;集合&#xff0c;索引列表和包。 这是我们的类层次结构的样子…

基于ARM的字符串拷贝实验(嵌入式系统)

基于ARM的字符串拷贝实验 一,实验目的 1.掌握ARM汇编指令LDR、STR和B等的使用方法,完成较为复杂的存储区访问和程序分支,学会使用条件码。 2.掌握完整的ARM汇编程序结构,具备初步的程序编写能力。 3.掌握ADS1.2集成开发环境的工程建立、编译参数设置、程序编译和调试等…

利用根升余弦滤波器和整数倍内插的多相结构生成含采样频偏的过采样信号

多相表示在多抽样率信号处理中是一种基本方法,使用它可以在实现整数倍和分数倍抽取和内插时提高计算效率。 目前我能想到的应用是信号的成型滤波,需要内插,然后与低通滤波器(成型时为过采样的(根)升余弦波形)进行卷积,这个过程如果直接去计算的话,会有很多多余的乘法操作…

java与java ee_CapeDwarf – Java EE上的Google App Engine

java与java ee我有很多爱好。 从早期的Java EE规范一路走来&#xff0c;并用Java EE 7进行了“云”之旅&#xff0c;我很好奇看到新宣布的CapeDwarf项目有什么库存&#xff0c;可以在内部引入Google的平台即服务&#xff0c;提供“ Google App Engine ” 。 到目前为止的故事 …

基于MATLAB的高阶(两个二阶级联构成的四阶以及更高阶)数字图形音频均衡器系数计算(可直接用于DSP实现)

引言 前不久,在数字信号处理中需要对音频信号进行滤波,涉及图形均衡器、参数均衡器的设计,下面这个链接给出了一个图形音频均衡器的例子: https://arm-software.github.io/CMSIS_5/DSP/html/group__GEQ5Band.html 这个示例演示如何使用 Biquad 级联函数构建 5 波段图形均衡…

基于MATLAB的曼彻斯特调制与解调实现

引言 曼彻斯特编码也称为相位编码,是一种同步时钟编码技术。通过电平的高低转换来表示“0”或“1”,每一位的中间有一个跳变的动作,这个动作既作时钟信号,又作数据信号,但因为每一个码元都被调成两个电平,所以数据传输速率只有调制速率的1/2,其编码效率为50%。常用于局…

【OFDM系列4】OFDM信号多径信道模型基础知识

多径信道模型(Multipath Channel Scenario) 信道脉冲响应(Channel Impulse Response, CIR) 信道的复基带脉冲响应如下所示 h ( τ ; t ) = ∑ l = 1 L a l ( t

kie-api_KIE-WB / JBPM控制台Ng –配置

kie-api大家好&#xff0c;这是我上一篇文章中有关如何使用jBPM Console的后续文章 。 这篇文章的主要思想是描述为了在您自己的公司中使用jBPM Console NG而需要进行的一些最常见的配置。 但是在讨论技术细节之前&#xff0c;我们将介绍KIE Workbench&#xff08;KIE-WB&#…

【OFDM系列5】单输入单输出OFDM(SISO-OFDM)多径信道迫零(ZF)和最小均方误差(MMSE)均衡器原理和公式推导

OFDM单输入单输出(SISO)迫零(ZF)均衡器 在去除CP之后,第k个子载波上的信号的FFT给出如下 Y k = H k D k + W k ⋯ ( 1 ) Y_k=H_k D_k+W_k\cdots(1)

如何在Tomcat中设置JNDI数据库连接池-Spring教程示例

在Spring和Tomcat中设置JNDI数据库连接池非常容易。 Tomcat服务器文档提供了有关如何在Tomcat 5、6或7中设置连接池的足够信息。在这里&#xff0c;我们将结合使用Tomcat 7和Spring框架在Tomcat服务器中创建连接池并在Spring中使用JNDI代码访问它们。 在上一篇文章中&#xff0…

java vm_Java VM –提防YoungGen空间

java vm正如您从我们以前的面向性能的文章中可能已经看到的那样&#xff0c;运行良好的JVM是实现最佳应用程序性能和稳定性的最重要目标之一。 这样的健康评估通常仅关注主要收集的频率&#xff08;避免&#xff09;或检测内存泄漏的存在。 年轻一代空间或短寿命物体的大小和足…

Java生产监控的阴暗面

自动化的工作流程是在敏捷环境中交付高质量产品的新金标准。 不幸的是&#xff0c;不断变化会带来不断的风险。 输入“可观察性”&#xff0c;这是一种度量&#xff0c;可以从系统的外部输出中推断出系统的内部状态。 知道应用程序如何在生产中运行的能力。 在本节中&#xff…

adf4350配置_配置MySQL以进行ADF开发

adf4350配置大家好。 今天&#xff0c;我将向您展示如何为Oracle ADF开发配置MySQL数据库。 恕我直言&#xff0c;当您将ADF与其他数据库而不是Oracle DB一起使用时&#xff0c;您将无法使用Oracle ADF的全部功能&#xff0c;有时您会发现自己在寻找解决方法&#xff0c;以实现…

如何在Java中使用重复项查找整数数组中的K个缺失数字?

自从我讨论任何编码或算法面试问题以来已经有很长时间了&#xff0c;因此我想重新考虑一种最流行的基于数组的编码问题&#xff0c;即在给定数组中查找缺失的数字。 在进行编程工作面试之前&#xff0c;您可能已经听说过或看到过此问题&#xff0c;但是面试官通常会使用许多不同…

基于同步压缩小波变换(Synchrosqueezed wavelet transforms)的时频分析的MATLAB实现(不使用内置函数wsst)

引言 同步压缩小波变换(Synchrosqueezed wavelet transforms, SST)采用了经验模态分解的设计思路,结合小波变换和reallocation theory构建而成,有明确的数学定义和推导。 EMD算法是一种旨在将它们分解成构建块函数的技术,这些构建块函数是(合理地)少量分量的叠加,这些分量…

垃圾收集算法,垃圾收集器_您正在使用什么垃圾收集器?

垃圾收集算法,垃圾收集器我们的研究实验室正全速前进。 随着最近的资本注入 &#xff0c;我们只能保证我们不断创新的步伐只会加快。 我们进行的部分研究与GC优化有关。 在处理这个有趣的领域中的问题时&#xff0c;我们认为可以分享一些有关GC算法使用的见解。 为此&#xff…

与时俱进:在JAX-RS API中采用OpenAPI v3.0.0

看到时间流逝真是太恐怖了&#xff01; OpenAPI规范3.0.0是对Swagger规范的重大修改&#xff0c;大部分已于一年前发布&#xff0c;但是工具赶上了一段时间。 但是&#xff0c;随着Swagger Core 2.0.0的最新正式发布&#xff0c;事情肯定会加速。 为了证明这一点&#xff0c;著…

jclouds_jclouds的命令行界面

jclouds序幕 我使用和为jclouds贡献了一年多的时间。 到目前为止&#xff0c;我已经在很多领域广泛使用了它&#xff0c;尤其是在Fuse生态系统中 。 它的强大之处在于它缺少一件事&#xff0c;该工具可用于管理jclouds也提供访问权限的任何云提供商。 类似于EC2命令之类的工具&…

【数字信号处理】离散傅里叶级数(DFS)

周期信号的DFS 周期信号一定 不存在 离散傅里叶变换,通过引入冲激序列,可以进行表示,使得数学运算更加严谨;但一定存在傅里叶级数! 时域周期==>频域离散 时域离散==>频域周期 时域又离散又周期==>频域又周期又离散 联系序列的傅里叶变换DFT理解即可,只不过复指…