Skip to content

Commit

Permalink
feat($Common): add new module Auth Center
Browse files Browse the repository at this point in the history
Add new module `Auth Center`: Authentication and Authorization Center
(AAC) for incoming requests from clients.

BREAKING CHANGE: support Redis operations
  • Loading branch information
johnnymillergh committed Mar 12, 2020
1 parent ce67791 commit 720475b
Show file tree
Hide file tree
Showing 55 changed files with 2,672 additions and 12 deletions.
8 changes: 8 additions & 0 deletions auth-center/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Getting Started

### Reference Documentation
For further reference, please consider the following sections:

* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/maven-plugin/)

132 changes: 132 additions & 0 deletions auth-center/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- Sub-module Basics -->
<artifactId>auth-center</artifactId>
<name>Auth Center</name>
<description>Authentication and Authorization Center (AAC) for incoming requests from clients.</description>
<properties>
<java.version>11</java.version>
<port>8770</port>
</properties>
<parent>
<groupId>com.jmsoftware</groupId>
<artifactId>muscle-and-fitness-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<!-- Build Settings -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

<!-- https://github.com/GoogleContainerTools/jib/blob/master/jib-maven-plugin/README.md -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.0.0</version>
<executions>
<!-- Bind `jib:dockerBuild` to a Maven lifecycle `compile`.
Jib will build your image directly to a Docker daemon. -->
<execution>
<id>compilingPhaseJib</id>
<phase>compile</phase>
<goals>
<goal>dockerBuild</goal>
</goals>
</execution>
<!-- Bind `jib:build` to a Maven lifecycle `package`.
Jib will build and push image to image registry. -->
<execution>
<id>packagingPhaseJib</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<from>
<image>openjdk:11.0.5-slim</image>
</from>
<to>
<image>docker.io/ijohnnymiller/${project.artifactId}-${envAlias}</image>
<tags>
<tag>${project.version}</tag>
</tags>
</to>
<container>
<!-- The root directory on the container where the app's contents are placed. -->
<appRoot>/${project.artifactId}-${envAlias}</appRoot>
<jvmFlags>
<jvmFlag>-Xmx256m</jvmFlag>
</jvmFlags>
<ports>
<port>${port}</port>
</ports>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
</plugin>
</plugins>
</build>

<!-- Scoped Dependencies Management -->
<dependencies>
<!-- Spring Frameworks -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
<groupId>com.jmsoftware</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

<!-- ORM Library -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.jmsoftware.authcenter;

import com.jmsoftware.authcenter.universal.configuration.ProjectProperty;
import com.jmsoftware.authcenter.universal.configuration.ServerConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

import java.time.Duration;
import java.time.Instant;
import java.util.TimeZone;

/**
* <h1>AuthCenterApplication</h1>
* <p>
* Change description here.
*
* @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com
* @date 3/12/20 9:57 AM
**/
@Slf4j
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class AuthCenterApplication {
private static final String LINE_SEPARATOR = System.lineSeparator();
private static ProjectProperty projectProperty;
private static ServerConfiguration serverConfiguration;

public AuthCenterApplication(ProjectProperty projectProperty, ServerConfiguration serverConfiguration) {
AuthCenterApplication.projectProperty = projectProperty;
AuthCenterApplication.serverConfiguration = serverConfiguration;
}

public static void main(String[] args) {
var startInstant = Instant.now();
SpringApplication.run(AuthCenterApplication.class, args);
var endInstant = Instant.now();
var duration = Duration.between(startInstant, endInstant);
log.info("🥳 Congratulations! 🎉");
log.info("🖥 {}@{} started!", projectProperty.getProjectArtifactId(), projectProperty.getVersion());
log.info("⚙️ Environment: {} ({})", projectProperty.getEnvironment(), projectProperty.getEnvironmentAlias());
log.info("⏳ Deployment duration: {} seconds ({} ms)", duration.getSeconds(), duration.toMillis());
log.info("⏰ App started at {} (timezone - {})", endInstant, TimeZone.getDefault().getDisplayName());
log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/",
LINE_SEPARATOR, LINE_SEPARATOR, serverConfiguration.getServerPort(), projectProperty.getContextPath(),
LINE_SEPARATOR, serverConfiguration.getBaseUrl());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package com.jmsoftware.authcenter.universal.aspect;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.jmsoftware.common.bean.ResponseBodyBean;
import com.jmsoftware.common.constant.HttpStatus;
import com.jmsoftware.common.exception.BaseException;
import com.jmsoftware.common.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.util.Objects;

/**
* <h1>ExceptionControllerAdvice</h1>
* <p>
* Exception advice for global controllers.
*
* @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com
* @date 2019-03-02 17:39
**/
@Slf4j
@ControllerAdvice
public class ExceptionControllerAdvice {
/**
* <p>Exception handler.</p>
* <p><strong>ATTENTION</strong>: In this method, <strong><em>cannot throw any exception</em></strong>.</p>
*
* @param request HTTP request
* @param exception any kinds of exception occurred in controller
* @return custom exception info
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public ResponseBodyBean<Object> handleException(HttpServletRequest request,
HttpServletResponse response,
Exception exception) {
log.error("Exception occurred when [{}] requested access. URL: {}",
RequestUtil.getRequestIpAndPort(request),
request.getServletPath());

// FIXME: THIS IS NOT A PROBLEM
// ATTENTION: Use only ResponseBodyBean.ofStatus() in handleException() method and DON'T throw any exception
if (exception instanceof NoHandlerFoundException) {
log.error("[GlobalExceptionCapture] NoHandlerFoundException: Request URL = {}, HTTP method = {}",
((NoHandlerFoundException) exception).getRequestURL(),
((NoHandlerFoundException) exception).getHttpMethod());
response.setStatus(HttpStatus.NOT_FOUND.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND);
} else if (exception instanceof HttpRequestMethodNotSupportedException) {
log.error("[GlobalExceptionCapture] HttpRequestMethodNotSupportedException: " +
"Current method is {}, Support HTTP method = {}",
((HttpRequestMethodNotSupportedException) exception).getMethod(),
JSONUtil.toJsonStr(
((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()));
response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.METHOD_NOT_ALLOWED);
} else if (exception instanceof MethodArgumentNotValidException) {
log.error("[GlobalExceptionCapture] MethodArgumentNotValidException: {}", exception.getMessage());
response.setStatus(HttpStatus.BAD_REQUEST.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(),
getFieldErrorMessageFromException((MethodArgumentNotValidException) exception),
null);
} else if (exception instanceof ConstraintViolationException) {
log.error("[GlobalExceptionCapture] ConstraintViolationException: {}", exception.getMessage());
response.setStatus(HttpStatus.BAD_REQUEST.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(),
CollUtil.getFirst(((ConstraintViolationException) exception)
.getConstraintViolations())
.getMessage(), null);
} else if (exception instanceof MethodArgumentTypeMismatchException) {
log.error("[GlobalExceptionCapture] MethodArgumentTypeMismatchException: " +
"Parameter name = {}, Exception information: {}",
((MethodArgumentTypeMismatchException) exception).getName(),
((MethodArgumentTypeMismatchException) exception).getMessage());
response.setStatus(HttpStatus.PARAM_NOT_MATCH.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_MATCH);
} else if (exception instanceof HttpMessageNotReadableException) {
log.error("[GlobalExceptionCapture] HttpMessageNotReadableException: {}",
((HttpMessageNotReadableException) exception).getMessage());
response.setStatus(HttpStatus.PARAM_NOT_NULL.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_NULL);
} else if (exception instanceof BaseException) {
log.error("[GlobalExceptionCapture] BaseException: Status code: {}, message: {}, data: {}",
((BaseException) exception).getCode(),
exception.getMessage(),
((BaseException) exception).getData());
response.setStatus(((BaseException) exception).getCode());
return ResponseBodyBean.ofStatus(((BaseException) exception).getCode(),
exception.getMessage(),
((BaseException) exception).getData());
} else if (exception instanceof BindException) {
log.error("[GlobalExceptionCapture]: Exception information: {} ", exception.getMessage());
response.setStatus(HttpStatus.INVALID_PARAM.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.INVALID_PARAM);
}
log.error("[GlobalExceptionCapture]: Exception information: {} ", exception.getMessage(), exception);
response.setStatus(HttpStatus.ERROR.getCode());
return ResponseBodyBean.ofStatus(HttpStatus.ERROR.getCode(), HttpStatus.ERROR.getMessage(), null);
}

/**
* Get field error message from exception. If two or more fields do not pass Spring Validation check, then will
* return the 1st error message of the error field.
*
* @param exception MethodArgumentNotValidException
* @return field error message
*/
private String getFieldErrorMessageFromException(MethodArgumentNotValidException exception) {
try {
DefaultMessageSourceResolvable firstErrorField =
(DefaultMessageSourceResolvable) Objects.requireNonNull(exception.getBindingResult()
.getAllErrors()
.get(0)
.getArguments())[0];
String firstErrorFieldName = firstErrorField.getDefaultMessage();
String firstErrorFieldMessage = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return firstErrorFieldName + " " + firstErrorFieldMessage;
} catch (Exception e) {
log.error("Exception occurred when get field error message from exception. Exception message: {}",
e.getMessage(),
e);
return HttpStatus.INVALID_PARAM.getMessage();
}
}
}
Loading

0 comments on commit 720475b

Please sign in to comment.