Skip to content

Spring boot security #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 218 additions & 34 deletions README.adoc

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
buildscript {
ext {
springBoot_1_X_Version = '1.5.13.RELEASE'
springBoot_2_X_Version = '2.1.3.RELEASE'
grpcVersion = '1.31.1'
springBoot_2_X_Version = '2.3.3.RELEASE'
grpcVersion = '1.32.1'
}
repositories {
mavenCentral()
Expand Down Expand Up @@ -39,6 +38,8 @@ allprojects {
mavenCentral()
jcenter()
maven { url "https://repo.spring.io/milestone" }
maven { url "https://jitpack.io" }

}
}

Expand Down Expand Up @@ -69,7 +70,8 @@ subprojects {
task codeCoverageReport(type: JacocoReport) {
executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")

sourceSets project('grpc-spring-boot-starter').sourceSets.main
sourceSets (project('grpc-spring-boot-starter').sourceSets.main
,project('grpc-client-spring-boot-starter').sourceSets.main)

//subprojects.each {
//sourceSets it.sourceSets.main
Expand All @@ -78,7 +80,7 @@ task codeCoverageReport(type: JacocoReport) {
reports {
xml.enabled true
xml.destination = new File(buildDir,"reports/jacoco/report.xml")
html.enabled false
html.enabled true
csv.enabled false
}
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=3.5.8-SNAPSHOT
version=4.0.0-SNAPSHOT
group=io.github.lognet
description=Spring Boot starter for Google RPC.
gitHubUrl=https\://github.com/LogNet/grpc-spring-boot-starter
Expand Down
119 changes: 119 additions & 0 deletions grpc-client-spring-boot-starter/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
buildscript {
repositories {
gradlePluginPortal()
mavenCentral()
}
dependencies {

classpath "io.franzbecker:gradle-lombok:4.0.0"
}
}

apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'signing'
apply plugin: "de.marcphilipp.nexus-publish"
apply plugin: 'io.franzbecker.gradle-lombok'

task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) {
def outputDir = file("$buildDir/delombok")
outputs.dir(outputDir)
for (srcDir in project.sourceSets.main.java.srcDirs) {
inputs.dir(srcDir)
args(srcDir, "-d", outputDir)
}
doFirst {
outputDir.deleteDir()
}
}

task sourceJar(type: Jar) {
classifier "sources"
from delombok
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier "javadoc"
from javadoc.destinationDir
}

artifacts {
archives jar
archives sourceJar
archives javadocJar
}
signing {
sign configurations.archives
}


nexusPublishing {
clientTimeout = java.time.Duration.ofMinutes(7)
repositories {
sonatype()
}

}

publishing {
publications {
mavenJava(MavenPublication) {
pom {
name = 'grpc-spring-boot-starter'
description = 'grpc-spring-boot-starter'
url = 'https://github.com/LogNet/grpc-spring-boot-starter'

scm {
url = 'https://github.com/LogNet/grpc-spring-boot-starter'
connection = 'scm:https://[email protected]/LogNet/grpc-spring-boot-starter.git'
developerConnection = 'scm:git://github.com/LogNet/grpc-spring-boot-starter.git'
}

licenses {
license {
name = 'The Apache Software License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution = 'repo'
}
}

developers {
developer {
id = 'jvmlet'
name = 'Furer Alexander'
email = '[email protected]'
}
}

}

from components.java


artifact(sourceJar) {
classifier = 'sources'
}
artifact(javadocJar) {
classifier = 'javadoc'
}
}
}
}

signing {
required {
// signing is required if this is a release version and the artifacts are to be published
!version.toString().endsWith('-SNAPSHOT') && tasks.withType(PublishToMavenRepository).find {
gradle.taskGraph.hasTask it
}
}
sign publishing.publications
}


dependencies {
compile "io.grpc:grpc-api:${grpcVersion}"
}
compileJava.dependsOn(processResources)


Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.lognet.springboot.grpc.security;

import io.grpc.CallCredentials;
import io.grpc.Metadata;
import io.grpc.Status;

import java.util.concurrent.Executor;

/**
* Adds Authorization header with configured configured authentication scheme token supplied by tokeSupplier
*/

public class AuthCallCredentials extends CallCredentials {
private AuthHeader authHeader;

public AuthCallCredentials(AuthHeader.AuthHeaderBuilder authHeaderBuilder) {
this(authHeaderBuilder.build());
}
public AuthCallCredentials(AuthHeader authHeader) {
this.authHeader = authHeader;
}

@Override
public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier metadataApplier) {

appExecutor.execute(()->{
try {
metadataApplier.apply(authHeader.attach(new Metadata()));
} catch (Throwable e) {
metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
}
}
);



}

@Override
public void thisUsesUnstableApi() {
// noop
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.lognet.springboot.grpc.security;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ClientInterceptors;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;

/**
* Adds Authorization header with configured authentication scheme token supplied by tokeSupplier to each intercepted client call
*/


public class AuthClientInterceptor implements ClientInterceptor {
private AuthHeader authHeader;

public AuthClientInterceptor(AuthHeader authHeader) {
this.authHeader = authHeader;
}
public AuthClientInterceptor(AuthHeader.AuthHeaderBuilder authHeaderBuilder) {
this(authHeaderBuilder.build());
}

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions, Channel next) {
return new ClientInterceptors.CheckedForwardingClientCall<ReqT, RespT>(next.newCall(methodDescriptor, callOptions)) {
@Override
protected void checkedStart(Listener<RespT> responseListener, Metadata headers) throws Exception {
delegate().start(responseListener, authHeader.attach(headers));
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.lognet.springboot.grpc.security;

import io.grpc.Metadata;
import lombok.Builder;

import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.function.Supplier;

@Builder
public class AuthHeader implements Constants {
private final Supplier<ByteBuffer> tokenSupplier;
private final String authScheme;

public static class AuthHeaderBuilder {

public AuthHeader.AuthHeaderBuilder bearer() {
return authScheme(Constants.BEARER_AUTH_SCHEME);
}
public AuthHeader.AuthHeaderBuilder basic() {
return authScheme(Constants.BASIC_AUTH_SCHEME);
}

public AuthHeader.AuthHeaderBuilder basic(String userName, byte[] password) {
final ByteBuffer buffer = ByteBuffer.allocate(userName.length() + password.length + 1)
.put(userName.getBytes())
.put((byte) ':')
.put(password);
buffer.rewind();
ByteBuffer token = Base64.getEncoder().encode(buffer);
return authScheme(Constants.BASIC_AUTH_SCHEME)
.tokenSupplier(() -> {
token.rewind();
return token;
});
}


}
public Metadata attach(Metadata metadataHeader){
byte[] token = tokenSupplier.get().array();
final byte[] header = ByteBuffer.allocate(authScheme.length() + token.length + 1)
.put(authScheme.getBytes())
.put((byte) ' ')
.put(token)
.array();
metadataHeader.put(Constants.AUTH_HEADER_KEY,header);
return metadataHeader;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.lognet.springboot.grpc.security;

import io.grpc.Metadata;


public interface Constants {
Metadata.Key<byte[]> AUTH_HEADER_KEY = Metadata.Key.of("Authorization"+Metadata.BINARY_HEADER_SUFFIX, Metadata.BINARY_BYTE_MARSHALLER);
String BEARER_AUTH_SCHEME="Bearer";
String BASIC_AUTH_SCHEME="Basic";



}
36 changes: 32 additions & 4 deletions grpc-spring-boot-starter-demo/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,51 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBoot_1_X_Version}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBoot_2_X_Version}")
}
}
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
ext {
set('springCloudVersion', "Hoxton.SR6")

}
dependencies {

compile "org.springframework.boot:spring-boot-starter-actuator"
compile 'org.springframework.boot:spring-boot-starter-web'

compile "org.springframework.security:spring-security-config"
compile "org.springframework.security:spring-security-oauth2-jose"
compile "org.springframework.security:spring-security-oauth2-resource-server"


compile project(':grpc-spring-boot-starter')
compile project(':grpc-client-spring-boot-starter')

testCompile 'org.springframework.boot:spring-boot-starter-aop'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'com.github.stefanbirkner:system-rules:1.18.0'
testCompile('org.springframework.cloud:spring-cloud-starter-consul-discovery')
testCompile 'com.pszymczyk.consul:embedded-consul:2.1.4'

testCompile "org.springframework.cloud:spring-cloud-config-server:2.1.1.RELEASE"
testCompile "org.springframework.cloud:spring-cloud-config-client:2.1.1.RELEASE"
testCompile "com.playtika.testcontainers:embedded-keycloak:1.76"
testRuntime "org.springframework.cloud:spring-cloud-starter"



testImplementation 'org.hamcrest:hamcrest:2.1'

testImplementation 'org.mockito:mockito-core:2.23.0'



//testCompile "org.testcontainers:junit-jupiter:1.14.3"

}
sourceSets {
main {
Expand Down Expand Up @@ -63,16 +91,16 @@ task cleanProtoGen{
}
}
clean.dependsOn cleanProtoGen
bootRepackage.enabled =false


dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-starter-parent:${springBoot_1_X_Version}"
mavenBom "org.springframework.boot:spring-boot-starter-parent:${springBoot_2_X_Version}"
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}






Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@
import io.grpc.examples.CalculatorOuterClass;
import io.grpc.stub.StreamObserver;
import org.lognet.springboot.grpc.GRpcService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoAppConfiguration {
@Bean
public GreeterService greeterService() {
return new GreeterService();
}


@GRpcService(interceptors = NotSpringBeanInterceptor.class)
public static class CalculatorService extends CalculatorGrpc.CalculatorImplBase{
Expand Down
Loading