packagestore.auth;importjava.util.Date;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.ResponseEntity;importorg.springframework.stereotype.Service;importstore.account.AccountController;importstore.account.AccountIn;importstore.account.AccountOut;@ServicepublicclassAuthService{@AutowiredprivateAccountControlleraccountController;@AutowiredprivateJwtServicejwtService;publicStringregister(Registerregister){AccountInaccountIn=AccountIn.builder().email(register.email()).name(register.name()).password(register.password()).build();// registrar no account// aqui estou substituindo o RestTemplateResponseEntity<AccountOut>response=accountController.create(accountIn);AccountOutaccountOut=response.getBody();// gerar o token// regra de geracao de tokenreturngenerateToken(accountOut.id());}publicStringlogin(Stringemail,Stringpassword){ResponseEntity<AccountOut>response=accountController.findByEmailAndPassword(AccountIn.builder().email(email).password(password).build());AccountOutaccountOut=response.getBody();returngenerateToken(accountOut.id());}privateStringgenerateToken(Stringid){DatenotBefore=newDate();Dateexpiration=newDate(notBefore.getTime()+1000l*60*60*24);Stringtoken=jwtService.create(id,notBefore,expiration);returntoken;}publicSolveOutsolve(Stringtoken){returnSolveOut.builder().idAccount(jwtService.getId(token)).build();}}
packagestore.auth;importjava.util.Date;importjavax.crypto.SecretKey;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Service;importio.jsonwebtoken.Claims;importio.jsonwebtoken.JwtParser;importio.jsonwebtoken.Jwts;importio.jsonwebtoken.io.Decoders;importio.jsonwebtoken.security.Keys;@ServicepublicclassJwtService{@Value("${store.jwt.issuer}")privateStringissuer;@Value("${store.jwt.secret-key}")privateStringsecretKey;publicStringcreate(Stringid,DatenotBefore,Dateexpiration){SecretKeykey=Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey));Stringjwt=Jwts.builder().header().and().id(id).issuer(issuer).signWith(key).notBefore(notBefore).expiration(expiration).compact();returnjwt;}publicStringgetId(Stringtoken){Claimsclaims=resolveClaims(token);Datenow=newDate();if(claims.getExpiration().before(now)){thrownewRuntimeException("Expired token");}if(claims.getNotBefore().after(now)){thrownewRuntimeException("Token is actived yet");}// check if token is revoked by a CRLreturnclaims.getId();}privateClaimsresolveClaims(Stringtoken){SecretKeykey=Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey));JwtParserparser=Jwts.parser().verifyWith(key).build();returnparser.parseSignedClaims(token).getPayload();}}
Security is an important aspect of software development. It involves protecting the confidentiality, integrity, and availability of data and resources. Two key concepts in security are authentication and authorization.
Authentication is the process of verifying the identity of a user or system. It ensures that the user or system is who they claim to be. Common authentication methods include passwords, biometrics, and two-factor authentication. The system checks these credentials against the stored data. If the credentials are valid, the system confirms the user's identity.
In many systems, after successful authentication, the system generates a token. This token is a piece of data that represents the user's authentication session. It's like a digital ticket that proves the user's identity for a certain period of time.
This token is then sent back to the user. The user's client software (like a web browser) stores this token and sends it along with every subsequent request to the server (in case of stateless server). This way, the server knows that the request comes from an authenticated user without needing to ask for the credentials again.
Here's a simplified step-by-step process:
The user sends their username and password (or other credentials) to the server;
The server verifies the credentials. If they're valid, the server generates a token.
The server sends this token back to the user.
The user's client software stores this token.
For every subsequent request, the client sends this token along with the request.
The server checks the token to ensure it's valid and hasn't expired.
This token-based authentication process is commonly used in many modern web applications and APIs. It helps maintain the user's session and allows the server to authenticate requests without storing the user's state.
Authorization is the process of granting or denying access to specific resources or actions based on the authenticated user's privileges. It determines what a user is allowed to do within a system. Authorization can be role-based, where permissions are assigned based on predefined roles, or attribute-based, where permissions are based on specific attributes of the user.
In many systems, the token not only represents the user's identity, but also includes information about their permissions or roles. This is often done using a type of token called a JSON Web Token (JWT), which can include a payload of data.
Here's a simplified step-by-step process:
After authentication, the user's client software sends a request to a server. This request includes the token.
The server decodes the token and extracts the user's identity and permissions.
The server checks whether the user has the necessary permissions for the requested action. This could involve checking the user's roles or other attributes against the requirements for the action.
If the user has the necessary permissions, the server allows the action. If not, the server denies the action.
This process allows the server to authorize actions without needing to repeatedly look up the user's permissions. It also allows for stateless servers, as the necessary information is included in every request.
By implementing strong authentication and authorization mechanisms, software systems can ensure that only authorized users have access to sensitive data and functionalities, reducing the risk of unauthorized access and potential security breaches.
As the platform has only one entrace point, it is
JWT is a decentralized
The point of entrance of API is the gateway, then as suggested by 8.
The gateway is responsible for the security of the system. It is the first point of contact for all incoming requests. The gateway is responsible for routing requests to the appropriate services and ensuring that only authorized users can access the system.
packagestore.gateway;importjava.net.InetAddress;importjava.net.NetworkInterface;importjava.net.SocketException;importjava.net.UnknownHostException;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.Enumeration;importjava.util.List;importjava.util.Map;importorg.springframework.http.HttpStatus;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublicclassGatewayResource{@GetMapping("/")publicResponseEntity<String>hallo(){returnnewResponseEntity<String>("api",HttpStatus.OK);}@GetMapping({"/info","/health-check"})publicResponseEntity<Map<String,String>>version(){Stringhostname="hostname can not be resolved";try{InetAddressaddr;addr=InetAddress.getLocalHost();hostname=addr.getHostName();}catch(UnknownHostExceptionex){}List<String>ips=newArrayList<String>();try{Enumeration<NetworkInterface>e=NetworkInterface.getNetworkInterfaces();while(e.hasMoreElements()){NetworkInterfacen=(NetworkInterface)e.nextElement();Enumeration<InetAddress>ee=n.getInetAddresses();while(ee.hasMoreElements()){InetAddressi=(InetAddress)ee.nextElement();ips.add(i.getCanonicalHostName()+": "+i.getHostAddress());}}}catch(SocketExceptione){}returnnewResponseEntity<Map<String,String>>(Map.ofEntries(Map.entry("hostname",hostname),Map.entry("ip",Arrays.toString(ips.toArray())),Map.entry("os.arch",System.getProperty("os.arch")),Map.entry("os.name",System.getProperty("os.name")),Map.entry("os.version",System.getProperty("os.version")),Map.entry("file.separator",System.getProperty("file.separator")),Map.entry("java.class.path",System.getProperty("java.class.path")),Map.entry("java.home",System.getProperty("java.home")),Map.entry("java.vendor",System.getProperty("java.vendor")),Map.entry("java.vendor.url",System.getProperty("java.vendor.url")),Map.entry("java.version",System.getProperty("java.version")),Map.entry("line.separator",System.getProperty("line.separator")),Map.entry("path.separator",System.getProperty("path.separator")),Map.entry("user.dir",System.getProperty("user.dir")),Map.entry("user.home",System.getProperty("user.home")),Map.entry("user.name",System.getProperty("user.name")),Map.entry("jar",newjava.io.File(GatewayApplication.class.getProtectionDomain().getCodeSource().getLocation().getPath()).toString())),HttpStatus.OK);}}
packagestore.gateway.security;importjava.util.Arrays;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.http.HttpHeaders;importorg.springframework.http.HttpStatus;importorg.springframework.http.MediaType;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.stereotype.Component;importorg.springframework.web.reactive.function.client.WebClient;importorg.springframework.web.server.ResponseStatusException;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;@ComponentpublicclassAuthorizationFilterimplementsGlobalFilter{privatestaticLoggerlogger=LoggerFactory.getLogger(AuthorizationFilter.class);privatestaticfinalStringAUTHORIZATION_HEADER="Authorization";privatestaticfinalStringAUTHORIZATION_BEARER_TOKEN_HEADER="Bearer";privatestaticfinalStringAUTH_SERVICE_TOKEN_SOLVE="http://auth:8080/auth/solve";@AutowiredprivateRouterValidatorrouterValidator;@AutowiredprivateWebClient.BuilderwebClient;@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){logger.debug("Entrou no filtro de authorization");ServerHttpRequestrequest=exchange.getRequest();if(!routerValidator.isSecured.test(request)){logger.debug("Rota nao eh segura");returnchain.filter(exchange);}logger.debug("Rota segura");if(!isAuthMissing(request)){logger.debug("Tem Authorization no Header");finalString[]parts=this.getAuthHeader(request).split(" ");if(parts.length!=2||!parts[0].equals(AUTHORIZATION_BEARER_TOKEN_HEADER)){logger.debug("Formato enviado: "+Arrays.toString(parts));thrownewResponseStatusException(HttpStatus.BAD_REQUEST,"Authorization header format must be: 'Bearer {token}'");}logger.debug("Resolver o token");returnrequestAuthTokenSolve(exchange,chain,parts[1]);}thrownewResponseStatusException(HttpStatus.UNAUTHORIZED,"Unauthorized");}privatebooleanisAuthMissing(ServerHttpRequestrequest){return!request.getHeaders().containsKey(AUTHORIZATION_HEADER);}privateStringgetAuthHeader(ServerHttpRequestrequest){returnrequest.getHeaders().getOrEmpty(AUTHORIZATION_HEADER).get(0);}// este metodo eh responsavel por enviar o token ao Auth Microservice// a fim de interpretar o token, a chamada eh feita via Rest.privateMono<Void>requestAuthTokenSolve(ServerWebExchangeexchange,GatewayFilterChainchain,Stringjwt){logger.debug("solving jwt: "+jwt);returnwebClient.defaultHeader(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE).build().post().uri(AUTH_SERVICE_TOKEN_SOLVE).bodyValue(TokenOut.builder().token(jwt).build()).retrieve().toEntity(SolveOut.class).flatMap(response->{if(response!=null&&response.hasBody()&&response.getBody()!=null){finalSolveOutout=response.getBody();logger.debug("id account"+out.idAccount());ServerWebExchangemodifiedExchange=this.updateRequest(exchange,out);returnchain.filter(modifiedExchange);}else{thrownewResponseStatusException(HttpStatus.UNAUTHORIZED,"Invalid token");}});}privateServerWebExchangeupdateRequest(ServerWebExchangeexchange,SolveOutout){logger.debug("original headers: "+exchange.getRequest().getHeaders().toString());ServerWebExchangemodified=exchange.mutate().request(exchange.getRequest().mutate().header("id-account",out.idAccount()).build()).build();logger.debug("updated headers: "+modified.getRequest().getHeaders().toString());returnmodified;}}
JWT stands for JSON Web Token. It is a compact, URL-safe means of representing claims between two parties. JWTs are commonly used to secure the transmission of information between parties in a web environment, typically for authentication and information exchange. The JWT specification is defined by RFC 75191 and it is a decentralized approach for security (which can support horizontal scalability).
Here are the key components and concepts of JWT:
JSON Format: JWTs are represented as JSON objects that are easy to parse and generate. The JSON format makes them human-readable and easy to work with.
Three Parts: JWTs consist of three parts separated by dots (.): Header, Payload, and Signature.
Header: The header typically consists of two parts: the type of the token (JWT) and the signing algorithm being used, such as HMAC SHA256 or RSA.
Payload: The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims.
Signature: To create the signature part, you take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
Encoding: Each of the three parts is Base64Url encoded, and the resulting strings are concatenated with periods between them. The final JWT looks like: xxxxx.yyyyy.zzzzz.
Stateless and Self-contained: JWTs are stateless, meaning that all the information needed is within the token itself. The server doesn't need to store the user's state. They are also self-contained, meaning that all the information needed is contained within the token.
Use Cases: JWTs are commonly used for authentication and information exchange between parties. For example, after a user logs in, a server could generate a JWT and send it to the client. The client can then include the JWT in the headers of subsequent requests to access protected resources. The server can verify the authenticity of the JWT using the stored secret key.
Security Considerations: While JWTs are widely used and versatile, it's important to handle them securely. For instance, the key used to sign the JWT should be kept secret, and HTTPS should be used to transmit JWTs to prevent man-in-the-middle attacks.
Here's a simple example of a JWT created on JWT Builder2:
JWTs are widely used in web development due to their simplicity, flexibility, and support across various programming languages and frameworks. They are commonly used in token-based authentication systems.