Skip to content
This repository has been archived by the owner on May 22, 2021. It is now read-only.

Commit

Permalink
Added spring-auth module (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
PAException committed May 19, 2020
1 parent c899ff6 commit ed95d43
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 0 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<module>scope</module>
<module>auth</module>
<module>util</module>
<module>spring-auth</module>
</modules>

<properties>
Expand Down
77 changes: 77 additions & 0 deletions spring-auth/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gewia-common</artifactId>
<groupId>com.gewia.common</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-auth</artifactId>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.10.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.gewia.common</groupId>
<artifactId>auth</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.gewia.common</groupId>
<artifactId>scope</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
<scope>compile</scope>
</dependency>

<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gewia.common.spring.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Authentication.class)
public @interface AuthScope {

String value() default "";

String scope() default "";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gewia.common.spring.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Authentication {

AuthScope[] value();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gewia.common.spring.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface IgnoreServiceToken {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.gewia.common.spring.auth;

import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@ComponentScan("com.gewia.common.spring.auth")
public abstract class SpringAuthentication implements InitializingBean {

@Getter(AccessLevel.PACKAGE) private static List<HandlerInterceptorAdapter> interceptors = new ArrayList<>();

@Override
public void afterPropertiesSet() {
interceptors = this.addAuthenticationInterceptors(interceptors);
}

abstract public List<HandlerInterceptorAdapter> addAuthenticationInterceptors(List<HandlerInterceptorAdapter> authenticationInterceptors);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.gewia.common.spring.auth;

import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Configuration
@EnableWebMvc
public class SpringAuthenticationWebConfig implements WebMvcConfigurer, HandlerMethodArgumentResolver {

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return ((HttpServletRequest) webRequest.getNativeRequest()).getAttribute("accessToken");
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(DecodedJWT.class);
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(this);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
for (HandlerInterceptorAdapter interceptors : SpringAuthentication.getInterceptors())
registry.addInterceptor(interceptors).addPathPatterns("/**/*");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.gewia.common.spring.auth.interceptor;

import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.gewia.common.auth.jwt.JwtUtil;
import com.gewia.common.spring.auth.AuthScope;
import com.gewia.common.spring.auth.Authentication;
import com.gewia.common.util.Pair;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@AllArgsConstructor
public class ScopeInterceptor extends HandlerInterceptorAdapter {

private final JwtUtil jwtUtil;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HandlerMethod method = (HandlerMethod) handler;

AuthScope[] authScopes;
Authentication auth = method.getMethodAnnotation(Authentication.class);
AuthScope methodAuthScope = method.getMethodAnnotation(AuthScope.class);
if (auth != null) authScopes = auth.value();
else {
if (methodAuthScope == null) return true;
authScopes = new AuthScope[]{methodAuthScope};
}


String jwt = request.getHeader("Authorization");
if (jwt == null || jwt.isBlank()) return false;

Pair<DecodedJWT, JwtUtil.VerificationResult> result = this.jwtUtil.verify(jwt);
switch (result.getRight()) {
case EXPIRED:
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
case INVALID:
response.setStatus(HttpStatus.NOT_ACCEPTABLE.value());
return false;
case FAILED:
response.setStatus(HttpStatus.EXPECTATION_FAILED.value());
return false;
case UNKNOWN:
response.setStatus(HttpStatus.FORBIDDEN.value());
return false;
default:
response.setStatus(HttpStatus.OK.value());
}

Claim claim = result.getLeft().getClaim("scopes");
List<String> userScopes = claim.asList(String.class);
for (AuthScope authScope : authScopes) {
String scope = authScope.scope();
if (scope.isBlank()) scope = authScope.value();
if (!scope.isBlank()) {
boolean isPresent = false;
for (String userScope : userScopes)
if (userScope.equalsIgnoreCase(scope)) {
isPresent = true;
break;
}
if (!isPresent) return false;
}
}

request.setAttribute("accessToken", result.getLeft());

return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.gewia.common.spring.auth.interceptor;

import com.gewia.common.spring.auth.IgnoreServiceToken;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@AllArgsConstructor
public class ServiceTokenInterceptor extends HandlerInterceptorAdapter {

private final String serviceToken;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setStatus(HttpStatus.FORBIDDEN.value());

HandlerMethod method = (HandlerMethod) handler;
if (method.hasMethodAnnotation(IgnoreServiceToken.class) ||
method.getMethod().getDeclaringClass().getAnnotation(IgnoreServiceToken.class) != null) {
response.setStatus(HttpStatus.OK.value());
return true;
}

String serviceToken = request.getHeader("X-ServiceToken");

if (serviceToken == null) return false;
if (!this.serviceToken.equals(serviceToken)) return false;

response.setStatus(HttpStatus.OK.value());
return true;
}

}

0 comments on commit ed95d43

Please sign in to comment.