add spring security and token management
This commit is contained in:
parent
597d4062c7
commit
c76c01a174
67
pom.xml
67
pom.xml
@ -17,6 +17,59 @@
|
|||||||
<java.version>11</java.version>
|
<java.version>11</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
<version>2.5.4</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-web</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security.oauth.boot</groupId>
|
||||||
|
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
|
||||||
|
<version>2.5.2</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-web</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-web</artifactId>
|
||||||
|
<version>5.3.9.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
<version>5.3.9.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.9.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.nimbusds</groupId>
|
||||||
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
|
<version>8.19</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
@ -34,20 +87,16 @@
|
|||||||
<artifactId>brave</artifactId>
|
<artifactId>brave</artifactId>
|
||||||
<version>5.16.0</version>
|
<version>5.16.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
<version>2.3</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>3.12.0</version>
|
<version>3.12.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.velocity</groupId>
|
|
||||||
<artifactId>velocity</artifactId>
|
|
||||||
<version>1.7</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.model.projection.UserRoleView;
|
||||||
|
import com.iconplus.smartproc.repository.RolesRepository;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
|
private final RolesRepository rolesRepository;
|
||||||
|
|
||||||
|
public CustomUserDetailsService(RolesRepository rolesRepository) {
|
||||||
|
this.rolesRepository = rolesRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) {
|
||||||
|
List<SimpleGrantedAuthority> authorities = null;
|
||||||
|
Optional<UserRoleView> userRole = rolesRepository.getUserRoleByUserId(username);
|
||||||
|
if (userRole.isPresent()) {
|
||||||
|
authorities=List.of(new SimpleGrantedAuthority(userRole.get().getRole()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new org.springframework.security.core.userdetails.User(username, username, authorities);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.iconplus.smartproc.exception.ErrorResponse;
|
||||||
|
import com.iconplus.smartproc.util.Constants;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Log4j2
|
||||||
|
public class JwtAuthenticationAccessDenied implements AccessDeniedHandler {
|
||||||
|
|
||||||
|
private static final String DEFAULT_CODE = "30000";
|
||||||
|
private static final String DEFAULT_MESSAGE = "Access denied";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException {
|
||||||
|
|
||||||
|
log.error("Access denied, {}", e.getMessage());
|
||||||
|
|
||||||
|
ErrorResponse errorResponse = new ErrorResponse();
|
||||||
|
errorResponse.setCode(DEFAULT_CODE);
|
||||||
|
errorResponse.setTitle(Constants.TITLE_INVALID_NEXT_STEP);
|
||||||
|
errorResponse.setMessage(DEFAULT_MESSAGE);
|
||||||
|
|
||||||
|
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
|
||||||
|
httpServletResponse.getOutputStream()
|
||||||
|
.println(new ObjectMapper().writeValueAsString(errorResponse));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.iconplus.smartproc.exception.ErrorResponse;
|
||||||
|
import com.iconplus.smartproc.util.Constants;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
AuthenticationException authException) throws IOException {
|
||||||
|
|
||||||
|
ErrorResponse errorResponse = new ErrorResponse();
|
||||||
|
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
|
||||||
|
var exception = (Exception) request.getAttribute("exception");
|
||||||
|
|
||||||
|
String message;
|
||||||
|
|
||||||
|
if (exception != null) {
|
||||||
|
|
||||||
|
if (exception.getCause() != null) {
|
||||||
|
message = exception.getCause().toString() + " " + exception.getMessage();
|
||||||
|
} else {
|
||||||
|
message = exception.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorResponse.setCode(Constants.ERR_CODE_40051);
|
||||||
|
errorResponse.setTitle(Constants.TITLE_INVALID_NEXT_STEP);
|
||||||
|
errorResponse.setMessage(message);
|
||||||
|
response.getOutputStream()
|
||||||
|
.println(new ObjectMapper().writeValueAsString(errorResponse));
|
||||||
|
} else {
|
||||||
|
errorResponse.setCode(Constants.ERR_CODE_80007);
|
||||||
|
errorResponse.setTitle(Constants.TITLE_INVALID_NEXT_STEP);
|
||||||
|
errorResponse.setMessage("Invalid Access Token");
|
||||||
|
response.getOutputStream()
|
||||||
|
.println(new ObjectMapper().writeValueAsString(errorResponse));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.exception.BusinessException;
|
||||||
|
import com.iconplus.smartproc.model.request.PostAccessTokenRequest;
|
||||||
|
import com.iconplus.smartproc.service.authentication.PostCheckAccessTokenService;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtRequestFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final JwtTokenUtil jwtTokenUtil;
|
||||||
|
private final PostCheckAccessTokenService postCheckAccessTokenService;
|
||||||
|
|
||||||
|
public JwtRequestFilter(JwtTokenUtil jwtTokenUtil,
|
||||||
|
PostCheckAccessTokenService postCheckAccessTokenService) {
|
||||||
|
this.jwtTokenUtil = jwtTokenUtil;
|
||||||
|
this.postCheckAccessTokenService = postCheckAccessTokenService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
try {
|
||||||
|
String jwtToken = extractJwtFromRequest(request);
|
||||||
|
if (StringUtils.hasText(jwtToken) && jwtTokenUtil.validateTokenOnly(jwtToken)) {
|
||||||
|
isValidToken(request, jwtToken);
|
||||||
|
UserDetails userDetails = new org.springframework.security.core.userdetails.User(jwtTokenUtil.getUsernameFromToken(jwtToken), "",
|
||||||
|
jwtTokenUtil.getRolesFromToken(jwtToken));
|
||||||
|
|
||||||
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails, null, userDetails.getAuthorities());
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
|
||||||
|
}
|
||||||
|
} catch(ExpiredJwtException | BadCredentialsException ex)
|
||||||
|
{
|
||||||
|
request.setAttribute("exception", ex);
|
||||||
|
}
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void isValidToken(HttpServletRequest request, String jwtToken) {
|
||||||
|
String requestUrl = request.getRequestURI();
|
||||||
|
String refreshTokenUrl = "/authentication-service/authentication/v1/refresh-token";
|
||||||
|
if (!refreshTokenUrl.equals(requestUrl)) {
|
||||||
|
var isValid = isValidAuthenticateToken(jwtToken);
|
||||||
|
if (!isValid) {
|
||||||
|
throw new BusinessException(HttpStatus.UNAUTHORIZED, "Invalid Access Token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidAuthenticateToken(String jwtToken) {
|
||||||
|
return postCheckAccessTokenService.execute(PostAccessTokenRequest.builder()
|
||||||
|
.accessToken(jwtToken)
|
||||||
|
.build()).getIsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractJwtFromRequest(HttpServletRequest request) {
|
||||||
|
String bearerToken = request.getHeader("Authorization");
|
||||||
|
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||||
|
return bearerToken.substring(7);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.model.token.TokenContent;
|
||||||
|
import io.jsonwebtoken.*;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtTokenUtil implements Serializable {
|
||||||
|
private static final long serialVersionUID = -2550185165626007488L;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private KeyPair keyPair;
|
||||||
|
|
||||||
|
//retrieve expiration date from jwt token
|
||||||
|
public Date getExpirationDateFromToken(String token) {
|
||||||
|
return getClaimFromToken(token, Claims::getExpiration);
|
||||||
|
}
|
||||||
|
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
|
||||||
|
final Claims claims = getAllClaimsFromToken(token);
|
||||||
|
return claimsResolver.apply(claims);
|
||||||
|
}
|
||||||
|
//for retrieveing any information from token we will need the secret key
|
||||||
|
public Claims getAllClaimsFromToken(String token) {
|
||||||
|
return Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean isTokenExpired(String token) {
|
||||||
|
final Date expiration = getExpirationDateFromToken(token);
|
||||||
|
return expiration.before(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateToken(String username, TokenContent tokenContent, Integer expirationInMs, String channel, String scopeType) {
|
||||||
|
Map<String, Object> claims = new HashMap<>();
|
||||||
|
|
||||||
|
claims.put("authorities", tokenContent.getAccessMenu());
|
||||||
|
claims.put("fullname", tokenContent.getFullname());
|
||||||
|
claims.put("username", tokenContent.getUsername());
|
||||||
|
claims.put("user_id", tokenContent.getUserId());
|
||||||
|
claims.put("role", tokenContent.getRole());
|
||||||
|
|
||||||
|
return doGenerateToken(claims, username, expirationInMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String doGenerateToken(Map<String, Object> claims, String subject, Integer expirationInMs) {
|
||||||
|
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
|
||||||
|
.setExpiration(new Date(System.currentTimeMillis() + expirationInMs))
|
||||||
|
.signWith(SignatureAlgorithm.RS256, keyPair.getPrivate()).compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||||
|
final String username = getUsernameFromToken(token);
|
||||||
|
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateTokenOnly(String authToken) {
|
||||||
|
try {
|
||||||
|
Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(authToken);
|
||||||
|
return true;
|
||||||
|
} catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException ex) {
|
||||||
|
throw new BadCredentialsException("INVALID_CREDENTIALS", ex);
|
||||||
|
} catch (ExpiredJwtException ex) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsernameFromToken(String token) {
|
||||||
|
Claims claims = Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody();
|
||||||
|
return claims.getSubject();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("squid:S4834")
|
||||||
|
public List<SimpleGrantedAuthority> getRolesFromToken(String token) {
|
||||||
|
Claims claims = Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody();
|
||||||
|
List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
|
||||||
|
ArrayList<String> authorities = claims.get("authorities", ArrayList.class);
|
||||||
|
authorities.forEach(c-> authorityList.add(new SimpleGrantedAuthority(c)));
|
||||||
|
return authorityList;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.util.RSAUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class KeyConfiguration {
|
||||||
|
|
||||||
|
@Value("${jwt.private-key}")
|
||||||
|
private String privateKey;
|
||||||
|
|
||||||
|
@Value("${jwt.public-key}")
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KeyPair keypairBean() {
|
||||||
|
|
||||||
|
PublicKey pubKey = RSAUtil.getPublicKey(publicKey);
|
||||||
|
PrivateKey privKey = RSAUtil.getPrivateKey(privateKey);
|
||||||
|
|
||||||
|
return new KeyPair(pubKey, privKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.iconplus.smartproc.configuration;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtAuthenticationAccessDenied jwtAuthenticationAccessDenied;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtRequestFilter jwtRequestFilter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||||
|
httpSecurity
|
||||||
|
.sessionManagement()
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
.sessionFixation().none().and()
|
||||||
|
.csrf().disable();
|
||||||
|
httpSecurity.authorizeRequests()
|
||||||
|
.antMatchers("/api*/**").permitAll()
|
||||||
|
.antMatchers("/actuator/health").permitAll()
|
||||||
|
.antMatchers("/swagger*/**").permitAll()
|
||||||
|
.antMatchers("/v2*/**").permitAll()
|
||||||
|
.antMatchers("/token/jwks.json").permitAll()
|
||||||
|
.anyRequest().authenticated().and()
|
||||||
|
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
|
||||||
|
.accessDeniedHandler(jwtAuthenticationAccessDenied);
|
||||||
|
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) throws Exception {
|
||||||
|
web.ignoring().antMatchers("/v2/api-docs",
|
||||||
|
"/configuration/ui",
|
||||||
|
"/swagger-resources/**",
|
||||||
|
"/configuration/security",
|
||||||
|
"/swagger-ui.html",
|
||||||
|
"/webjars/**");
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ public class UsersController {
|
|||||||
.orElseThrow(() -> new ResourceNotFoundException("Users not exist with id :" + id));
|
.orElseThrow(() -> new ResourceNotFoundException("Users not exist with id :" + id));
|
||||||
|
|
||||||
users.setUsername(usersDetails.getUsername());
|
users.setUsername(usersDetails.getUsername());
|
||||||
users.setNama(usersDetails.getNama());
|
users.setFullname(usersDetails.getFullname());
|
||||||
users.setEmail(usersDetails.getEmail());
|
users.setEmail(usersDetails.getEmail());
|
||||||
users.setPassword(usersDetails.getPassword());
|
users.setPassword(usersDetails.getPassword());
|
||||||
// users.setInstansi(usersDetails.getInstansi());
|
// users.setInstansi(usersDetails.getInstansi());
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package com.iconplus.smartproc.exception;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class ErrorDetails {
|
|
||||||
private Date timestamp;
|
|
||||||
private String message;
|
|
||||||
private String details;
|
|
||||||
|
|
||||||
public ErrorDetails(Date timestamp, String message, String details) {
|
|
||||||
super();
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.message = message;
|
|
||||||
this.details = details;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDetails() {
|
|
||||||
return details;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ import org.apache.velocity.Template;
|
|||||||
import org.apache.velocity.VelocityContext;
|
import org.apache.velocity.VelocityContext;
|
||||||
import org.apache.velocity.app.Velocity;
|
import org.apache.velocity.app.Velocity;
|
||||||
import org.apache.velocity.app.VelocityEngine;
|
import org.apache.velocity.app.VelocityEngine;
|
||||||
|
import org.apache.velocity.context.Context;
|
||||||
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
|
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
|
||||||
import org.apache.velocity.runtime.resource.util.StringResourceRepository;
|
import org.apache.velocity.runtime.resource.util.StringResourceRepository;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -88,7 +89,7 @@ public class ErrorHelper {
|
|||||||
.getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT);
|
.getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT);
|
||||||
resourceRepository.putStringResource(RAW_TEMPLATE, rawNotificationTemplate);
|
resourceRepository.putStringResource(RAW_TEMPLATE, rawNotificationTemplate);
|
||||||
|
|
||||||
return new VelocityContext(parameters);
|
return new VelocityContext((Context) parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addVelocityProperties() {
|
private void addVelocityProperties() {
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.iconplus.smartproc.helper.service;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.JWSObject;
|
||||||
|
import com.nimbusds.jose.Payload;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.minidev.json.JSONObject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
public class TokenUtils {
|
||||||
|
|
||||||
|
private TokenUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> decodeToken(String jwtToken) {
|
||||||
|
Map<String, String> body = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (jwtToken != null) {
|
||||||
|
jwtToken = jwtToken.replace("Bearer", "");
|
||||||
|
jwtToken = jwtToken.trim();
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(jwtToken)) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
JWSObject token = JWSObject.parse(jwtToken);
|
||||||
|
|
||||||
|
Payload tokenPayload = token.getPayload();
|
||||||
|
JSONObject tokenBody = tokenPayload.toJSONObject();
|
||||||
|
|
||||||
|
tokenBody.forEach((key, value) -> {
|
||||||
|
if (Objects.isNull(value)) {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
body.put(key, value.toString());
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to parse JWT Token. Error: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getValueByParam(String param, String token) {
|
||||||
|
return decodeToken(token).get(param);
|
||||||
|
}
|
||||||
|
}
|
52
src/main/java/com/iconplus/smartproc/model/entity/Menu.java
Normal file
52
src/main/java/com/iconplus/smartproc/model/entity/Menu.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package com.iconplus.smartproc.model.entity;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseEntity;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.GeneratorType;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Entity
|
||||||
|
@Table(name = "permission")
|
||||||
|
public class Menu extends BaseEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "subMenu")
|
||||||
|
private String subMenu;
|
||||||
|
|
||||||
|
@Column(name = "menu")
|
||||||
|
private String menu;
|
||||||
|
|
||||||
|
// @Column(name = "can_view")
|
||||||
|
// @Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
// private Boolean canView;
|
||||||
|
//
|
||||||
|
// @Column(name = "can_read")
|
||||||
|
// @Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
// private Boolean canRead;
|
||||||
|
//
|
||||||
|
// @Column(name = "can_create")
|
||||||
|
// @Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
// private Boolean canCreate;
|
||||||
|
//
|
||||||
|
// @Column(name = "can_delete")
|
||||||
|
// @Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
// private Boolean canDelete;
|
||||||
|
|
||||||
|
@Column(name = "deleted")
|
||||||
|
@Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
private Boolean deleted;
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.iconplus.smartproc.model.entity;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseEntity;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Table(name = "permission")
|
||||||
|
public class Permission extends BaseEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(generator = "UUID")
|
||||||
|
@GenericGenerator(
|
||||||
|
name = "UUID",
|
||||||
|
strategy = "org.hibernate.id.UUIDGenerator"
|
||||||
|
)
|
||||||
|
@Column(name = "id")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column(name = "role_id")
|
||||||
|
private Long roleId;
|
||||||
|
|
||||||
|
@Column(name = "role_id")
|
||||||
|
private Long menuId;
|
||||||
|
|
||||||
|
@Column(name = "deleted")
|
||||||
|
@Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
private Boolean deleted;
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.iconplus.smartproc.model.entity;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseEntity;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Table(name = "token_management")
|
||||||
|
public class TokenManagement extends BaseEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(generator = "UUID")
|
||||||
|
@GenericGenerator(
|
||||||
|
name = "UUID",
|
||||||
|
strategy = "org.hibernate.id.UUIDGenerator"
|
||||||
|
)
|
||||||
|
@Column(name = "id")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column(name = "user_id")
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column(name = "access_token")
|
||||||
|
private String accessToken;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column(name = "refresh_token")
|
||||||
|
private String refreshToken;
|
||||||
|
|
||||||
|
@Column(name = "issued_time")
|
||||||
|
private Timestamp issuedTime;
|
||||||
|
|
||||||
|
@Column(name = "expired_time")
|
||||||
|
private Timestamp expiredTime;
|
||||||
|
|
||||||
|
@Column(name = "deleted")
|
||||||
|
@Type(type = "org.hibernate.type.NumericBooleanType")
|
||||||
|
private Boolean deleted;
|
||||||
|
}
|
@ -20,8 +20,8 @@ public class Users extends BaseEntity {
|
|||||||
@Column(name = "username")
|
@Column(name = "username")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Column(name = "nama")
|
@Column(name = "fullname")
|
||||||
private String nama;
|
private String fullname;
|
||||||
|
|
||||||
@Column(name = "email")
|
@Column(name = "email")
|
||||||
private String email;
|
private String email;
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.iconplus.smartproc.model.projection;
|
||||||
|
|
||||||
|
import java.sql.Clob;
|
||||||
|
|
||||||
|
public interface TokenManagementView {
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
void setId(String id);
|
||||||
|
|
||||||
|
String getUserId();
|
||||||
|
void setUserId(String userId);
|
||||||
|
|
||||||
|
Clob getAccessToken();
|
||||||
|
void setAccessToken(Clob accessToken);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.iconplus.smartproc.model.projection;
|
||||||
|
|
||||||
|
public interface UserRoleView {
|
||||||
|
|
||||||
|
Long getId();
|
||||||
|
String getUsername();
|
||||||
|
String getPassword();
|
||||||
|
Long getRoleId();
|
||||||
|
String getRole();
|
||||||
|
Boolean getDeleted();
|
||||||
|
|
||||||
|
void setId(Long id);
|
||||||
|
void setUsername(String username);
|
||||||
|
void setPassword(String password);
|
||||||
|
void setRoleId(Long roleId);
|
||||||
|
void setRole(String role);
|
||||||
|
void setDeleted(Boolean deleted);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.iconplus.smartproc.model.request;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseRequest;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoginRequest extends BaseRequest {
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
private String password;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.iconplus.smartproc.model.request;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseRequest;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PostAccessTokenRequest extends BaseRequest {
|
||||||
|
|
||||||
|
private String accessToken;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.iconplus.smartproc.model.response;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseResponse;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class LoginResponse extends BaseResponse {
|
||||||
|
|
||||||
|
private String accessToken;
|
||||||
|
private String validity;
|
||||||
|
private String refreshToken;
|
||||||
|
private String username;
|
||||||
|
private String email;
|
||||||
|
private Long roleId;
|
||||||
|
private String role;
|
||||||
|
Set<String> accessMenu;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.iconplus.smartproc.model.response;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.base.BaseResponse;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class PostAccessTokenResponse extends BaseResponse {
|
||||||
|
|
||||||
|
private Boolean isValid;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.iconplus.smartproc.model.token;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
public class TokenContent {
|
||||||
|
private String userId;
|
||||||
|
private String role;
|
||||||
|
private Set<String> accessMenu;
|
||||||
|
private String fullname;
|
||||||
|
private String username;
|
||||||
|
}
|
@ -1,10 +1,26 @@
|
|||||||
package com.iconplus.smartproc.repository;
|
package com.iconplus.smartproc.repository;
|
||||||
|
|
||||||
import com.iconplus.smartproc.model.entity.Roles;
|
import com.iconplus.smartproc.model.entity.Roles;
|
||||||
|
import com.iconplus.smartproc.model.projection.UserRoleView;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RolesRepository extends JpaRepository<Roles, Long> {
|
public interface RolesRepository extends JpaRepository<Roles, Long> {
|
||||||
|
|
||||||
|
@Query(value = "SELECT u.id as id, " +
|
||||||
|
"u.username as username, " +
|
||||||
|
"u.password as password, " +
|
||||||
|
"u.roleId as roleId, " +
|
||||||
|
"r.role as role " +
|
||||||
|
"FROM Users u " +
|
||||||
|
"join Roles r on r.id=u.roleId " +
|
||||||
|
"WHERE u.deleted = false " +
|
||||||
|
"AND r.deleted = false " +
|
||||||
|
"AND u.username = :userName ")
|
||||||
|
Optional<UserRoleView> getUserRoleByUserId(String userName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.iconplus.smartproc.repository;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.model.entity.TokenManagement;
|
||||||
|
import com.iconplus.smartproc.model.projection.TokenManagementView;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface TokenManagementRepository extends JpaRepository<TokenManagement, String> {
|
||||||
|
|
||||||
|
@Query(value = "SELECT access_token as accessToken FROM token_management " +
|
||||||
|
"WHERE user_id = :userId " +
|
||||||
|
"AND deleted = false " +
|
||||||
|
"AND expired_time >= SYSDATE", nativeQuery = true)
|
||||||
|
List<TokenManagementView> findAccessTokenByUserIdAndDeletedFalse(String userId);
|
||||||
|
}
|
@ -4,7 +4,11 @@ import com.iconplus.smartproc.model.entity.Users;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface UsersRepository extends JpaRepository<Users, Long> {
|
public interface UsersRepository extends JpaRepository<Users, Long> {
|
||||||
|
|
||||||
|
Optional<Users> findByEmailAndDeletedFalse(String email);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.iconplus.smartproc.service.authentication;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.exception.BusinessException;
|
||||||
|
import com.iconplus.smartproc.helper.service.BaseService;
|
||||||
|
import com.iconplus.smartproc.model.request.LoginRequest;
|
||||||
|
import com.iconplus.smartproc.model.response.LoginResponse;
|
||||||
|
import com.iconplus.smartproc.repository.UsersRepository;
|
||||||
|
import com.iconplus.smartproc.util.Constants;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class LoginService implements BaseService<LoginRequest, LoginResponse> {
|
||||||
|
|
||||||
|
|
||||||
|
private final UsersRepository usersRepository;
|
||||||
|
|
||||||
|
public LoginService(UsersRepository userRepository) {
|
||||||
|
this.usersRepository = userRepository;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginResponse execute(LoginRequest input) {
|
||||||
|
|
||||||
|
var user= usersRepository.findByEmailAndDeletedFalse(input.getEmail())
|
||||||
|
.orElseThrow(() -> new BusinessException(HttpStatus.CONFLICT,
|
||||||
|
Constants.ERR_CODE_10003,
|
||||||
|
Constants.ERR_TTL_10003,
|
||||||
|
String.format(Constants.ERR_MSG_10003, input.getEmail())));
|
||||||
|
|
||||||
|
if (!StringUtils.equalsIgnoreCase(input.getPassword(), user.getPassword())) {
|
||||||
|
throw new BusinessException(HttpStatus.CONFLICT,
|
||||||
|
Constants.ERR_CODE_10004,
|
||||||
|
Constants.ERR_TTL_10004,
|
||||||
|
Constants.ERR_MSG_10004);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return LoginResponse.builder()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.iconplus.smartproc.service.authentication;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.helper.service.BaseService;
|
||||||
|
import com.iconplus.smartproc.helper.service.TokenUtils;
|
||||||
|
import com.iconplus.smartproc.model.projection.TokenManagementView;
|
||||||
|
import com.iconplus.smartproc.model.request.PostAccessTokenRequest;
|
||||||
|
import com.iconplus.smartproc.model.response.PostAccessTokenResponse;
|
||||||
|
import com.iconplus.smartproc.repository.TokenManagementRepository;
|
||||||
|
import com.iconplus.smartproc.util.CommonUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class PostCheckAccessTokenService implements BaseService<PostAccessTokenRequest, PostAccessTokenResponse> {
|
||||||
|
|
||||||
|
private TokenManagementRepository tokenManagementRepository;
|
||||||
|
|
||||||
|
public PostCheckAccessTokenService(TokenManagementRepository tokenManagementRepository) {
|
||||||
|
this.tokenManagementRepository = tokenManagementRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PostAccessTokenResponse execute(PostAccessTokenRequest input) {
|
||||||
|
|
||||||
|
var decodeToken = TokenUtils.decodeToken(input.getAccessToken());
|
||||||
|
String userId = decodeToken.get("user_id");
|
||||||
|
|
||||||
|
List<TokenManagementView> tokenManagementViews = tokenManagementRepository.findAccessTokenByUserIdAndDeletedFalse(userId);
|
||||||
|
if (tokenManagementViews.isEmpty()) {
|
||||||
|
log.error("access token not found in db");
|
||||||
|
return PostAccessTokenResponse.builder()
|
||||||
|
.isValid(false)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> userTokenList = tokenManagementViews.stream()
|
||||||
|
.map(c-> CommonUtil.clobToString(c.getAccessToken()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
boolean isMatch = userTokenList.stream().anyMatch(s -> s.equals(input.getAccessToken()));
|
||||||
|
if (isMatch) {
|
||||||
|
return PostAccessTokenResponse.builder()
|
||||||
|
.isValid(true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PostAccessTokenResponse.builder()
|
||||||
|
.isValid(false)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/com/iconplus/smartproc/util/CommonUtil.java
Normal file
25
src/main/java/com/iconplus/smartproc/util/CommonUtil.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.iconplus.smartproc.util;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.exception.TechnicalException;
|
||||||
|
|
||||||
|
import java.sql.Clob;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public class CommonUtil {
|
||||||
|
private CommonUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clobToString(Clob input) {
|
||||||
|
try {
|
||||||
|
if (input != null) {
|
||||||
|
return input.getSubString(1, (int) input.length());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (SQLException exception) {
|
||||||
|
throw new TechnicalException(Constants.ERR_TTL_40041,
|
||||||
|
Constants.ERR_TTL_40041,
|
||||||
|
Constants.ERR_MSG_40041);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,4 +12,21 @@ public class Constants {
|
|||||||
public static final String ERR_TTL_10002 = "Data tersedia";
|
public static final String ERR_TTL_10002 = "Data tersedia";
|
||||||
public static final String ERR_MSG_10002 = "Jenis Anggaran dengan id : %s sudah tersedia";
|
public static final String ERR_MSG_10002 = "Jenis Anggaran dengan id : %s sudah tersedia";
|
||||||
|
|
||||||
|
public static final String ERR_CODE_10003 = "10003";
|
||||||
|
public static final String ERR_TTL_10003 = "Data tidak tersedia";
|
||||||
|
public static final String ERR_MSG_10003 = "User dengan email : %s tidak ditemukan";
|
||||||
|
|
||||||
|
public static final String ERR_CODE_10004 = "10004";
|
||||||
|
public static final String ERR_TTL_10004 = "Gagal Authentikasi User";
|
||||||
|
public static final String ERR_MSG_10004 = "Silahkan Periksa kembali Email dan Password Anda";
|
||||||
|
|
||||||
|
public static final String ERR_CODE_40041 = "40041";
|
||||||
|
public static final String ERR_TTL_40041 = "Terjadi Gangguan";
|
||||||
|
public static final String ERR_MSG_40041 = "Masalah Koneksi System";
|
||||||
|
|
||||||
|
public static final String ERR_CODE_40051 = "40051";
|
||||||
|
public static final String ERR_CODE_80007 = "80007";
|
||||||
|
public static final String TITLE_INVALID_NEXT_STEP = "Proses tidak dapat dilanjutkan";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
109
src/main/java/com/iconplus/smartproc/util/RSAUtil.java
Normal file
109
src/main/java/com/iconplus/smartproc/util/RSAUtil.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package com.iconplus.smartproc.util;
|
||||||
|
|
||||||
|
import com.iconplus.smartproc.exception.TechnicalException;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
public class RSAUtil {
|
||||||
|
|
||||||
|
private RSAUtil(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey getPrivateKey(String base64PrivateKey){
|
||||||
|
PrivateKey privateKey = null;
|
||||||
|
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
|
||||||
|
try {
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
privateKey = keyFactory.generatePrivate(keySpec);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
log.error("InvalidKeySpecException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "InvalidKeySpecException : "+e.getMessage());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
}
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicKey getPublicKey(String base64PublicKey){
|
||||||
|
PublicKey publicKey = null;
|
||||||
|
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
|
||||||
|
try {
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
publicKey = keyFactory.generatePublic(keySpec);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
log.error("InvalidKeySpecException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "InvalidKeySpecException : "+e.getMessage());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
}
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decryptChipper(byte[] data, PrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||||
|
return new String(cipher.doFinal(data));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
} catch (NoSuchPaddingException e) {
|
||||||
|
log.error("NoSuchPaddingException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "NoSuchPaddingException : "+e.getMessage());
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
log.error("InvalidKeyException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "InvalidKeyException : "+e.getMessage());
|
||||||
|
} catch (IllegalBlockSizeException e) {
|
||||||
|
log.error("IllegalBlockSizeException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "IllegalBlockSizeException : "+e.getMessage());
|
||||||
|
} catch (BadPaddingException e) {
|
||||||
|
log.error("BadPaddingException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "BadPaddingException : "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] encryptChipper(byte[] data, PublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
|
return cipher.doFinal(data);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
log.error("NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "NoSuchAlgorithmException : "+e.getMessage());
|
||||||
|
} catch (NoSuchPaddingException e) {
|
||||||
|
log.error("NoSuchPaddingException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "NoSuchPaddingException : "+e.getMessage());
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
log.error("InvalidKeyException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "InvalidKeyException : "+e.getMessage());
|
||||||
|
} catch (IllegalBlockSizeException e) {
|
||||||
|
log.error("IllegalBlockSizeException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "IllegalBlockSizeException : "+e.getMessage());
|
||||||
|
} catch (BadPaddingException e) {
|
||||||
|
log.error("BadPaddingException : "+e.getMessage());
|
||||||
|
throw new TechnicalException("80000", "BadPaddingException : "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decrypt(String data, String base64PrivateKey) {
|
||||||
|
return decryptChipper(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encrypt(String data, String base64PublicKey) {
|
||||||
|
return Base64.getEncoder().encodeToString(encryptChipper(data.getBytes(), getPublicKey(base64PublicKey)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
src/main/resources/application-local.yml
Normal file
22
src/main/resources/application-local.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#Configuration for database
|
||||||
|
jwt:
|
||||||
|
public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3MRA7zxvaWKrtmPl2hRJLFiyryvj0ZUlmWw9OZIgqwJUDBTsg5yFX4hCQrANV1yy5ibTqAn2APdNCdhGgp8R2YLWrUR2vVGbmnKXXzEDsFpT6cgo+/a+lWaUq8aYEhzVg8Xjmy9oG1s521LklEz/jKD2xNE4OgY2Y1SgfUH+bexs84ZEyUhcSpCrcZenjCns7Ubp9zsWPrXa/j6kr7ZuFWH7nXN/i+oYF0HFhc+hDaVr2R9Q7s56wAQNE8XfI2Q+h4iRI1hWa5Vva9ha1DnCN9McJDLBHoGy1coIUEoQKkDhCNQmdHlubIJYDoFIyfDTrSQIXw2gzrrC9sOgYU64wIDAQAB
|
||||||
|
private-key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHcxEDvPG9pYqu2Y+XaFEksWLKvK+PRlSWZbD05kiCrAlQMFOyDnIVfiEJCsA1XXLLmJtOoCfYA900J2EaCnxHZgtatRHa9UZuacpdfMQOwWlPpyCj79r6VZpSrxpgSHNWDxeObL2gbWznbUuSUTP+MoPbE0Tg6BjZjVKB9Qf5t7GzzhkTJSFxKkKtxl6eMKeztRun3OxY+tdr+PqSvtm4VYfudc3+L6hgXQcWFz6ENpWvZH1DuznrABA0Txd8jZD6HiJEjWFZrlW9r2FrUOcI30xwkMsEegbLVyghQShAqQOEI1CZ0eW5sglgOgUjJ8NOtJAhfDaDOusL2w6BhTrjAgMBAAECggEAL8SPPqXx/8m1/Up96XVCeddCIewu39GoUJzqVL0SgLlTQbFRWkO7bwpWPyQkBKPs6nYSPDJ/WG0UfXkw+FuqiC8YaREUxawYwjj0Do/jJOWNo1YTqy+28l2uKD9nwceYV/QlYCcLA3Exs/upLdHk8eyHR+DFjlgIG5KNQDK/Rj8cLdQGJjAZjHUGzaRq/HhDi+0LVkOoUvzbzle8RSiiZnNrxCt0UV1CambQzvyxXzqLgtGe4s+qItwk/zzkvXDRnRju3+/AyaFaToA2Wnp9gdV2rT4cRGHhj0Dqh0vwrBH4RiPtEfLQjIa9DhTPHfpr6ACGe6GF96fJ889WIEl4gQKBgQDrGgYKGwIhY9j2V7tu6eHmLxYFGzxSPOtfBZPzSLUyQXrcd6wFlb+Fsae2bgBFZJzilIv4lG0wNaGSFjAjteyXMoqVCTIUKJCjzWs+g3qjuA5fu9IAeWEEaXuKSVvkV5tJrnwhmqYdH1jQx5xhpO434W2a4wuEgOnU6SzNjvS/TwKBgQDZLb3Q9KRwlMcyt/rgjeTOf2SD1jvlKD2t+YRuaxlzBs7j99hCEP3GLaTz6/rIptfkutwk10FM/piO5PCyoPXNc2A1Z73ukmsirMRy2G06PfqKBQrV/B9vtIqPW1Agz7KUpXARrHL8Bb6T2ljKl2HU+kC4Pgx2GZQgYnbEvNBGLQKBgQCNf3grFN5PYlzuxxbURofjlmtWX3IKvQechSrqvdPwj2B22L/8DIjc2nPqZIJdQZT6+hTUY+DjpyO7XQdUNuZSieTGlmZBo7iKHdRyJ4fkiZ59F/notyUhFqt4K3bXE7MuwYmMYBuiY399COaiDjYiA9eH5SbFdSFHN5/ziBreGQKBgGZHNG2DyS1745PvMScvq+HT/PJZojt5iBK2v7eAmKujOSwDPMVgWyNJu7VkHOcCLAp8NdDjzs0D2bTx/KkjRJ9NBrIf+UKxkeLymlG7uzUCm0sEtOWxptxkmhyJVGMfbWqzvuOT41LtIaNf4REH2fsDIBekoRm9UhUuSeC9SxjBAoGAP6pdT7HlMsr++XJFqs9+dj7tRsX+a6Coi3T8BFaf2Nl9YxMFFaSBbNOO4QfZWyCUcfbkLrcdEka5VTVt/HwckDKzckxWqH3hMn2+kAKJnHhAvB1M38KAyvdpA54vNgVyID+vL5VbqpHk8wsUbVWZc8F/ERELfuHwzcD0rt6Opnc=
|
||||||
|
expired-time :
|
||||||
|
service-account:
|
||||||
|
access-token: 480
|
||||||
|
refresh-token: 720
|
||||||
|
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
url: jdbc:postgresql://localhost:5432/smartproc
|
||||||
|
username: postgres
|
||||||
|
password: postgre
|
||||||
|
jackson:
|
||||||
|
default-property-inclusion: NON_NULL
|
||||||
|
jpa:
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
||||||
|
show-sql: true
|
@ -1,12 +0,0 @@
|
|||||||
spring.datasource.url=jdbc:postgresql://localhost:5432/smartproc
|
|
||||||
spring.datasource.username=postgres
|
|
||||||
spring.datasource.password=postgre
|
|
||||||
spring.jpa.show-sql=true
|
|
||||||
|
|
||||||
## Hibernate Properties
|
|
||||||
# The SQL dialect makes Hibernate generate better SQL for the chosen database
|
|
||||||
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
|
|
||||||
|
|
||||||
# Hibernate ddl auto (create, create-drop, validate, update)
|
|
||||||
spring.jpa.hibernate.ddl-auto = update
|
|
||||||
server.port=9090
|
|
7
src/main/resources/application.yml
Normal file
7
src/main/resources/application.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: "smartproc-service"
|
||||||
|
profiles:
|
||||||
|
active: local
|
||||||
|
server:
|
||||||
|
port: 9090
|
5
src/main/resources/bootstrap.properties
Normal file
5
src/main/resources/bootstrap.properties
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
spring.application.name=smartproc
|
||||||
|
spring.profiles.active=@spring.profiles.active@
|
||||||
|
management.endpoints.web.exposure.include=*
|
||||||
|
management.endpoint.health.show-details=ALWAYS
|
||||||
|
management.metrics.tags.application=${spring.application.name}
|
Loading…
x
Reference in New Issue
Block a user