parent
12893d3cc9
commit
5e8e150066
@ -0,0 +1,69 @@
|
|||||||
|
package com.xit.biz.ctgy.auth;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.repository.IMinUserinfoRepository;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MinPasswordEncoder implements PasswordEncoder {
|
||||||
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private String userid;
|
||||||
|
private final IMinUserinfoRepository repository;
|
||||||
|
|
||||||
|
// public MinPasswordEncoder(IMinUserinfoRepository repository) {
|
||||||
|
// this.repository = repository;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비밀번호 암호화
|
||||||
|
* @param rawPassword
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encode(CharSequence rawPassword) {
|
||||||
|
if (rawPassword == null) {
|
||||||
|
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||||
|
}
|
||||||
|
return repository.queryGetPasswdEncode(rawPassword.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 암호화 되지 않은 비밀 번호와 암호화된 비밀번호 일치 여부 비교
|
||||||
|
* @param rawPassword
|
||||||
|
* @param encodedPassword
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||||
|
//return false;
|
||||||
|
|
||||||
|
if (rawPassword == null) {
|
||||||
|
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||||
|
}
|
||||||
|
if (encodedPassword == null || encodedPassword.length() == 0) {
|
||||||
|
this.logger.warn("Empty encoded password");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Objects.equals(repository.queryGetPasswd(rawPassword.toString()), encodedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 암호화된 비밀번호를 다시 암호화 하고자 할 경우 true return
|
||||||
|
* @param encodedPassword
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean upgradeEncoding(String encodedPassword) {
|
||||||
|
return PasswordEncoder.super.upgradeEncoding(encodedPassword);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package com.xit.biz.ctgy.auth;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import com.xit.core.oauth2.oauth.entity.ProviderType;
|
||||||
|
import com.xit.core.oauth2.oauth.entity.RoleType;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserMinPrincipal implements OAuth2User, UserDetails, OidcUser {
|
||||||
|
private final String userId;
|
||||||
|
private final String password;
|
||||||
|
private final ProviderType providerType;
|
||||||
|
private final RoleType roleType;
|
||||||
|
private final Collection<GrantedAuthority> authorities;
|
||||||
|
private Map<String, Object> attributes;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccountNonLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCredentialsNonExpired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getClaims() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OidcUserInfo getUserInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OidcIdToken getIdToken() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserMinPrincipal create(MinUserinfo user) {
|
||||||
|
return new UserMinPrincipal(
|
||||||
|
user.getUserid(),
|
||||||
|
user.getPasswd(),
|
||||||
|
ProviderType.LOCAL,
|
||||||
|
RoleType.USER,
|
||||||
|
Collections.singletonList(new SimpleGrantedAuthority(RoleType.USER.getCode()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserMinPrincipal create(MinUserinfo user, Map<String, Object> attributes) {
|
||||||
|
UserMinPrincipal userPrincipal = create(user);
|
||||||
|
userPrincipal.setAttributes(attributes);
|
||||||
|
|
||||||
|
return userPrincipal;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.xit.biz.ctgy.auth.service;
|
||||||
|
|
||||||
|
import com.xit.biz.cmm.entity.CmmUser;
|
||||||
|
import com.xit.biz.ctgy.dto.LoginMinRequestDto;
|
||||||
|
import com.xit.core.oauth2.api.dto.LoginRequestDto;
|
||||||
|
import com.xit.core.oauth2.api.dto.TokenDto;
|
||||||
|
import com.xit.core.oauth2.api.dto.TokenRequestDto;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface IAuthMinService {
|
||||||
|
|
||||||
|
//TokenDto login(final CmmUserRequestDto cmmUserRequestDto);
|
||||||
|
TokenDto login(final LoginMinRequestDto loginRequestDto,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
HttpSession session);
|
||||||
|
|
||||||
|
TokenDto reissue(final TokenRequestDto tokenRequestDto, HttpServletRequest request, HttpServletResponse response);
|
||||||
|
|
||||||
|
boolean validationToken(final String accessToken, final boolean isExceptionThrow);
|
||||||
|
|
||||||
|
Map<String, Object> findAccessTokenInfo(final String accessToken);
|
||||||
|
|
||||||
|
Optional<CmmUser> findMyUserWithoutAuthorities();
|
||||||
|
|
||||||
|
Optional<CmmUser> findUserWithAuthorities(final String userId);
|
||||||
|
}
|
@ -0,0 +1,312 @@
|
|||||||
|
package com.xit.biz.ctgy.auth.service.impl;
|
||||||
|
|
||||||
|
import com.xit.biz.cmm.entity.CmmUser;
|
||||||
|
import com.xit.biz.ctgy.auth.service.IAuthMinService;
|
||||||
|
import com.xit.biz.ctgy.dto.LoginMinRequestDto;
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import com.xit.biz.ctgy.repository.IMinUserinfoRepository;
|
||||||
|
import com.xit.core.constant.ErrorCode;
|
||||||
|
import com.xit.core.constant.XitConstants;
|
||||||
|
import com.xit.core.exception.TokenAuthException;
|
||||||
|
import com.xit.core.oauth2.api.dto.TokenDto;
|
||||||
|
import com.xit.core.oauth2.api.dto.TokenRequestDto;
|
||||||
|
import com.xit.core.oauth2.api.entity.RefreshToken;
|
||||||
|
import com.xit.core.oauth2.api.repository.RefreshTokenRepository;
|
||||||
|
import com.xit.core.oauth2.config.properties.AppProperties;
|
||||||
|
import com.xit.core.oauth2.oauth.JwtTokenProvider;
|
||||||
|
import com.xit.core.oauth2.utils.CookieUtil;
|
||||||
|
import com.xit.core.oauth2.utils.HeaderUtil;
|
||||||
|
import com.xit.core.util.Checks;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthMinService implements IAuthMinService {
|
||||||
|
@Value("${xit.auth.save.type:header}")
|
||||||
|
private String authSaveType;
|
||||||
|
|
||||||
|
@Value("${jwt.refresh.save.type:HEADER}")
|
||||||
|
private String tokenParamType;
|
||||||
|
|
||||||
|
private static final int EXPIRE_CONVERT_SECOND_FROM_DAY = 60 * 60 * 24;
|
||||||
|
private final static long THREE_DAYS_MILISECONDS = 3 * (1000 * 60 * 60 * 24);
|
||||||
|
|
||||||
|
private final AppProperties appProperties;
|
||||||
|
private final AuthenticationManager authenticationManager;
|
||||||
|
private final IMinUserinfoRepository userRepository;
|
||||||
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
|
private final RefreshTokenRepository refreshTokenRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 로그인 요청을 처리(Redis에 저장)하고 토큰(Access + Refresh) return
|
||||||
|
*
|
||||||
|
* 1. Login ID, PW 로 인증 정보 객체 UsernamePasswordAuthenticationToken 생성
|
||||||
|
* 2. AuthenticationManager에 authenticate 메소드의 파라미터로 넘겨, 검증 후 Authentication(사용자ID가 들어있다) return
|
||||||
|
* AuthenticationManager --> 스프링 시큐리티의 실제 인증이 이루어지는 곳
|
||||||
|
* authenticate 메소드 하나만 정의되어 있는 인터페이스
|
||||||
|
* Builder 에서 UserDetails 의 유저 정보가 서로 일치하는지 검사
|
||||||
|
* 3. 인증정보로 JWT 토큰 생성
|
||||||
|
* 4. Refresh 토큰 저장 : : Redis에 저장
|
||||||
|
* 5. 토큰(Access + Refresh) return
|
||||||
|
*
|
||||||
|
* @see AuthenticationManagerBuilder
|
||||||
|
* @see JwtTokenProvider
|
||||||
|
* </pre>
|
||||||
|
* @param loginRequestDto LoginMinRequestDto
|
||||||
|
* @return TokenDto
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public TokenDto login(final LoginMinRequestDto loginRequestDto,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
HttpSession session) {
|
||||||
|
TokenDto tokenDto = null;
|
||||||
|
Authentication authentication = null;
|
||||||
|
|
||||||
|
// 1. Login ID/PW 를 기반으로 AuthenticationToken 생성
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken = loginRequestDto.toAuthentication();
|
||||||
|
|
||||||
|
// 2. 실제로 검증 (사용자 비밀번호 체크) 이 이루어지는 부분
|
||||||
|
authentication = authenticationManager.authenticate(authenticationToken);
|
||||||
|
|
||||||
|
// Authentication 저장
|
||||||
|
if(Objects.equals(authSaveType, XitConstants.AuthSaveType.SECURITY.getCode())){
|
||||||
|
// TODO :: SessionCreationPolicy.STATELESS 인 경우 사용 불가
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
|
||||||
|
}else if(Objects.equals(authSaveType, XitConstants.AuthSaveType.SESSION.getCode())){
|
||||||
|
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
MinUserinfo user = userRepository.findByUserid(loginRequestDto.getUserid()).orElseThrow(() -> new UsernameNotFoundException(loginRequestDto.getUserid() + " -> 사용자를 찾을 수 없습니다."));
|
||||||
|
Map<String, Object> infoMap = new HashMap<>();
|
||||||
|
infoMap.put("userName", user.getName());
|
||||||
|
infoMap.put("userEmail", user.getEmail());
|
||||||
|
|
||||||
|
RefreshToken savedRefreshToken = refreshTokenRepository.findByKey(authentication.getName())
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
// 저장된 refresh token not exists
|
||||||
|
if (Checks.isNull(savedRefreshToken) || Checks.isNull(savedRefreshToken.getValue())) {
|
||||||
|
// 없는 경우 새로 등록
|
||||||
|
tokenDto = jwtTokenProvider.generateTokenDto(authentication, infoMap);
|
||||||
|
refreshTokenRepository.saveAndFlush(
|
||||||
|
RefreshToken.builder()
|
||||||
|
.key(loginRequestDto.getUserid())
|
||||||
|
.value(tokenDto.getRefreshToken())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
// 저장된 토큰이 있다
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// 만료되지 않은 refresh token
|
||||||
|
if(!jwtTokenProvider.isExpiredToken(savedRefreshToken.getValue())) {
|
||||||
|
|
||||||
|
// refresh token 잔여기간 기준 도달여부 check
|
||||||
|
Date now = new Date();
|
||||||
|
long validTime = jwtTokenProvider.parseClaims(savedRefreshToken.getValue()).getExpiration().getTime() - now.getTime();
|
||||||
|
|
||||||
|
if (validTime <= THREE_DAYS_MILISECONDS) {
|
||||||
|
// 새로운 토큰 생성 : access token and refresh token
|
||||||
|
tokenDto = jwtTokenProvider.generateTokenDto(authentication, infoMap);
|
||||||
|
|
||||||
|
// DB에 refresh 토큰 업데이트
|
||||||
|
savedRefreshToken.updateValue(Objects.requireNonNull(tokenDto.getRefreshToken()));
|
||||||
|
|
||||||
|
}else{
|
||||||
|
tokenDto = TokenDto.builder()
|
||||||
|
.grantType(XitConstants.JwtToken.GRANT_TYPE.getCode())
|
||||||
|
.accessToken(jwtTokenProvider.generateJwtAccessToken(authentication, infoMap))
|
||||||
|
.refreshToken(null)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
refreshTokenRepository.saveAndFlush(
|
||||||
|
RefreshToken.builder()
|
||||||
|
.key(loginRequestDto.getUserid())
|
||||||
|
.value(tokenDto.getRefreshToken())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh token 만료
|
||||||
|
}else{
|
||||||
|
tokenDto = jwtTokenProvider.generateTokenDto(authentication, infoMap);
|
||||||
|
|
||||||
|
refreshTokenRepository.saveAndFlush(
|
||||||
|
RefreshToken.builder()
|
||||||
|
.key(loginRequestDto.getUserid())
|
||||||
|
.value(tokenDto.getRefreshToken())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// COOKIE 타입의 요청인 경우 COOKIE set
|
||||||
|
if(Objects.equals(XitConstants.JwtTokenParamType.COOKIE.name(), tokenParamType)) {
|
||||||
|
CookieUtil.deleteCookie(request, response, XitConstants.JwtToken.REFRESH_TOKEN_NAME.getCode());
|
||||||
|
CookieUtil.addCookie(response, XitConstants.JwtToken.REFRESH_TOKEN_NAME.getCode(), tokenDto.getRefreshToken(), appProperties.getAuth().getRefreshTokenExpiry() * EXPIRE_CONVERT_SECOND_FROM_DAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 토큰 발급
|
||||||
|
return tokenDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 토큰 재발급 : Redis에 저장
|
||||||
|
* 1. Access 토큰 검증
|
||||||
|
* 2. Access Token 복호화 --> 유저 정보 (Member ID) GET
|
||||||
|
* 3. Refresh Token 만료 여부 검증
|
||||||
|
* 4. 저장소의 Refresh Token GET
|
||||||
|
* 5. 저장소의 Refresh Token == 전달받은 Refresh Token 의 일치 여부 확인
|
||||||
|
* 6. 토큰 생성
|
||||||
|
* 7. Refresh Token update & 저장소 저장
|
||||||
|
* 8. 토큰 return
|
||||||
|
*
|
||||||
|
* @see JwtTokenProvider
|
||||||
|
* </pre>
|
||||||
|
* @param tokenRequestDto TokenRequestDto
|
||||||
|
* @return TokenDto
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public TokenDto reissue(final TokenRequestDto tokenRequestDto, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
TokenDto tokenDto = new TokenDto();
|
||||||
|
String sAccessToken = null;
|
||||||
|
String sRefreshToken = null;
|
||||||
|
Authentication authentication = null;
|
||||||
|
|
||||||
|
//-------------------------------------------------------
|
||||||
|
// access token 검증
|
||||||
|
//-------------------------------------------------------
|
||||||
|
// Get access token -------------------------------------
|
||||||
|
if(Objects.equals(XitConstants.JwtTokenParamType.DTO.name(), tokenParamType))
|
||||||
|
sAccessToken = tokenRequestDto.getAccessToken();
|
||||||
|
|
||||||
|
else if(
|
||||||
|
Objects.nonNull(request) &&
|
||||||
|
(Objects.equals(XitConstants.JwtTokenParamType.COOKIE.name(), tokenParamType)
|
||||||
|
|| Objects.equals(XitConstants.JwtTokenParamType.HEADER.name(), tokenParamType))
|
||||||
|
)
|
||||||
|
sAccessToken = HeaderUtil.getAccessToken(request);
|
||||||
|
else
|
||||||
|
throw new TokenAuthException("Token 전달 형태가 미 정의 되었습니다.");
|
||||||
|
|
||||||
|
// Access token check -------------------------------------
|
||||||
|
// 오염된 토큰 인지만 체크
|
||||||
|
jwtTokenProvider.validateTokenExcludeExpired(sAccessToken, false, true);
|
||||||
|
//if(!jwtTokenProvider.validateToken(sAccessToken, false, false)) throw new TokenAuthException(ErrorCode.INVALID_TOKEN);
|
||||||
|
// expired access token 여부 체크
|
||||||
|
// TODO :: 유효기간 만료된 경우에만 재발급 - 테스트를 위해 반대로 발급되도록 함
|
||||||
|
if(jwtTokenProvider.isExpiredToken(sAccessToken)) throw new TokenAuthException(ErrorCode.NOT_EXPIRED_TOKEN_YET);
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
authentication = jwtTokenProvider.getAuthentication(sAccessToken);
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// refresh token 검증
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// Get refresh token --------------------------------------
|
||||||
|
if(Objects.equals(XitConstants.JwtTokenParamType.COOKIE.name(), tokenParamType))
|
||||||
|
sRefreshToken = CookieUtil.getCookie(request, XitConstants.JwtToken.REFRESH_TOKEN_NAME.getCode())
|
||||||
|
.map(Cookie::getValue)
|
||||||
|
.orElse((null));
|
||||||
|
else if(Objects.equals(XitConstants.JwtTokenParamType.HEADER.name(), tokenParamType)
|
||||||
|
|| Objects.equals(XitConstants.JwtTokenParamType.DTO.name(), tokenParamType))
|
||||||
|
sRefreshToken = tokenRequestDto.getRefreshToken();
|
||||||
|
|
||||||
|
// Refresh token check ------------------------------------
|
||||||
|
jwtTokenProvider.validateTokenExcludeExpired(sRefreshToken, true, true);
|
||||||
|
|
||||||
|
// userId refresh token 으로 DB 확인
|
||||||
|
RefreshToken savedRefreshToken = refreshTokenRepository.findByKey(authentication.getName())
|
||||||
|
.orElse(null);
|
||||||
|
if (Checks.isEmpty(savedRefreshToken)) throw new TokenAuthException(ErrorCode.NOT_EXISTS_SAVED_REFRESH_TOKEN);
|
||||||
|
|
||||||
|
if (!Objects.equals(Objects.requireNonNull(savedRefreshToken).getValue(), sRefreshToken)) {
|
||||||
|
throw new TokenAuthException(ErrorCode.MISMATCH_REFRESH_ACCESS_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 토큰 부가 정보 set
|
||||||
|
MinUserinfo user = userRepository.findByUserid(authentication.getName()).orElse(null);
|
||||||
|
if(Checks.isNull(user)) throw new TokenAuthException(ErrorCode.INVALID_ROLE_TOKEN);
|
||||||
|
Map<String, Object> infoMap = new HashMap<>();
|
||||||
|
infoMap.put("userName", user.getName());
|
||||||
|
infoMap.put("userEmail", user.getEmail());
|
||||||
|
|
||||||
|
// TODO : refresh토큰 발급 기준인 정의 필요
|
||||||
|
// refresh 토큰 기간이 3일 이하로 남은 경우, refresh 토큰 갱신
|
||||||
|
Date now = new Date();
|
||||||
|
long validTime = jwtTokenProvider.parseClaims(sRefreshToken).getExpiration().getTime() - now.getTime();
|
||||||
|
|
||||||
|
if (validTime <= THREE_DAYS_MILISECONDS) {
|
||||||
|
// 토큰 생성 : access token and refresh token
|
||||||
|
tokenDto = jwtTokenProvider.generateTokenDto(authentication, infoMap);
|
||||||
|
|
||||||
|
// TODO : DB 갱신 확인 필요
|
||||||
|
// DB refresh 토큰 업데이트
|
||||||
|
RefreshToken newRefreshToken = savedRefreshToken.updateValue(Objects.requireNonNull(tokenDto.getRefreshToken()));
|
||||||
|
//refreshTokenRepository.saveAndFlush(newRefreshToken);
|
||||||
|
|
||||||
|
if(Objects.equals(XitConstants.JwtTokenParamType.COOKIE.name(), tokenParamType)) {
|
||||||
|
CookieUtil.deleteCookie(request, response, XitConstants.JwtToken.REFRESH_TOKEN_NAME.getCode());
|
||||||
|
CookieUtil.addCookie(response, XitConstants.JwtToken.REFRESH_TOKEN_NAME.getCode(), newRefreshToken.getValue(), appProperties.getAuth().getRefreshTokenExpiry() * EXPIRE_CONVERT_SECOND_FROM_DAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
tokenDto = TokenDto.builder()
|
||||||
|
.grantType(XitConstants.JwtToken.GRANT_TYPE.getCode())
|
||||||
|
.accessToken(jwtTokenProvider.generateJwtAccessToken(authentication, infoMap))
|
||||||
|
.refreshToken(null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return tokenDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public boolean validationToken(final String accessToken, final boolean isExceptionThrow) {
|
||||||
|
return jwtTokenProvider.validateTokenExcludeExpired(accessToken, false, isExceptionThrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO :: 적용 필요
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Map<String, Object> findAccessTokenInfo(String accessToken) {
|
||||||
|
return jwtTokenProvider.getAccessTokenInfo(accessToken); //authTokenProvider.getAccessTokenInfo(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<CmmUser> findMyUserWithoutAuthorities() {
|
||||||
|
//cmmUserRepos
|
||||||
|
return Optional.empty(); //cmmUserRepository.findOneWithAuthorities(SecurityUtil.getCurrentMemberId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<CmmUser> findUserWithAuthorities(final String userId) {
|
||||||
|
return Optional.empty(); //cmmUserRepository.findOneWithAuthorities(userId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package com.xit.biz.ctgy.auth.service.impl;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.auth.UserMinPrincipal;
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import com.xit.biz.ctgy.repository.IMinUserinfoRepository;
|
||||||
|
import com.xit.core.oauth2.oauth.entity.ProviderType;
|
||||||
|
import com.xit.core.oauth2.oauth.info.AbstractOAuth2UserInfo;
|
||||||
|
import com.xit.core.oauth2.oauth.info.OAuth2UserInfoFactory;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||||
|
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomMinOAuth2UserService extends DefaultOAuth2UserService {
|
||||||
|
|
||||||
|
private final IMinUserinfoRepository userRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
||||||
|
OAuth2User user = super.loadUser(userRequest);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return this.process(userRequest, user);
|
||||||
|
} catch (AuthenticationException ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
throw new InternalAuthenticationServiceException(ex.getLocalizedMessage(), ex.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2User process(OAuth2UserRequest userRequest, OAuth2User user) {
|
||||||
|
// TODO :: 적용 여부 파악 필요
|
||||||
|
ProviderType providerType = ProviderType.valueOf(userRequest.getClientRegistration().getRegistrationId().toUpperCase());
|
||||||
|
|
||||||
|
AbstractOAuth2UserInfo userInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(providerType, user.getAttributes());
|
||||||
|
MinUserinfo savedUser = null;
|
||||||
|
Optional<MinUserinfo> optionalCmmUser = userRepository.findByUserid(userInfo.getId());
|
||||||
|
|
||||||
|
if(optionalCmmUser.isPresent()) {
|
||||||
|
// savedUser = optionalCmmUser.get();
|
||||||
|
// if (Objects.equals(providerType, savedUser.getProviderType())) {
|
||||||
|
// throw new OAuthProviderMissMatchException(
|
||||||
|
// "Looks like you're signed up with " + providerType +
|
||||||
|
// " account. Please use your " + savedUser.getProviderType() + " account to login."
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// updateUser(savedUser, userInfo);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
savedUser = createUser(userInfo, providerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (optionalCmmUser.isEmpty()) {
|
||||||
|
// if (providerType != savedUser.getProviderType()) {
|
||||||
|
// throw new OAuthProviderMissMatchException(
|
||||||
|
// "Looks like you're signed up with " + providerType +
|
||||||
|
// " account. Please use your " + savedUser.getProviderType() + " account to login."
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// updateUser(savedUser, userInfo);
|
||||||
|
// } else {
|
||||||
|
// savedUser = createUser(userInfo, providerType);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return UserMinPrincipal.create(savedUser, user.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MinUserinfo createUser(AbstractOAuth2UserInfo userInfo, ProviderType providerType) {
|
||||||
|
MinUserinfo user = MinUserinfo.builder()
|
||||||
|
.userid(userInfo.getId())
|
||||||
|
.name(userInfo.getName())
|
||||||
|
.email(userInfo.getEmail())
|
||||||
|
.build();
|
||||||
|
return userRepository.saveAndFlush(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void updateUser(MinUserinfo user, AbstractOAuth2UserInfo userInfo) {
|
||||||
|
// if (Objects.nonNull(userInfo.getName()) && !Objects.equals(userInfo.getName(), user.getUserName())) {
|
||||||
|
// user.setUserName(userInfo.getName());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (Objects.nonNull(userInfo.getImageUrl()) && !Objects.equals(userInfo.getImageUrl(), user.getProfileImageUrl())) {
|
||||||
|
// user.setProfileImageUrl(userInfo.getImageUrl());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.xit.biz.ctgy.auth.service.impl;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.auth.UserMinPrincipal;
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import com.xit.biz.ctgy.repository.IMinUserinfoRepository;
|
||||||
|
import com.xit.core.constant.ErrorCode;
|
||||||
|
import com.xit.core.exception.CustomBaseException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomMinUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
|
private final IMinUserinfoRepository userRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
MinUserinfo user = userRepository.findByUserid(username).orElseThrow(() -> new CustomBaseException(ErrorCode.USER_NOT_FOUND));
|
||||||
|
return UserMinPrincipal.create(user);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.xit.biz.ctgy.controller;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.dto.MinCivBoard680Dto;
|
||||||
|
import com.xit.biz.ctgy.dto.MinUserinfoDto;
|
||||||
|
import com.xit.biz.ctgy.dto.struct.MinCivBoard680Mapstruct;
|
||||||
|
import com.xit.biz.ctgy.dto.struct.MinUserinfoMapstruct;
|
||||||
|
import com.xit.biz.ctgy.service.IMinCivBoard680Service;
|
||||||
|
import com.xit.biz.ctgy.service.IMinUserinfoService;
|
||||||
|
import com.xit.core.api.IRestResponse;
|
||||||
|
import com.xit.core.api.RestResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@Tag(name = "MinUserinfoController", description = "사용자 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/ctgy/user")
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MinUserinfoController {
|
||||||
|
|
||||||
|
private final IMinUserinfoService service;
|
||||||
|
|
||||||
|
private final MinUserinfoMapstruct mapstruct = Mappers.getMapper(MinUserinfoMapstruct.class);
|
||||||
|
|
||||||
|
// TODO :: 파라메터 정의 필요
|
||||||
|
@Operation(summary = "사용자 목록 조회" , description = "사용자 목록 조회")
|
||||||
|
@Parameters({
|
||||||
|
@Parameter(in = ParameterIn.QUERY, name = "userid", description = "사용자ID", required = false, example = " "),
|
||||||
|
@Parameter(in = ParameterIn.QUERY, name = "name", description = "이름", required = false, example = " "),
|
||||||
|
@Parameter(in = ParameterIn.QUERY, name = "page", description = "페이지", required = true, example = "1"),
|
||||||
|
@Parameter(in = ParameterIn.QUERY, name = "size", description = "페이지당갯수", required = true, example = "10")
|
||||||
|
})
|
||||||
|
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public ResponseEntity<? extends IRestResponse> findMinUserinfos(
|
||||||
|
@Parameter(hidden = true)
|
||||||
|
@ModelAttribute("minUserinfoDto")
|
||||||
|
final MinUserinfoDto minUserinfoDto,
|
||||||
|
@Parameter(hidden = true)
|
||||||
|
final Pageable pageable) {
|
||||||
|
return RestResponse.of(service.findMinUserinfos(mapstruct.toEntity(minUserinfoDto), pageable));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
package com.xit.biz.ctgy.controller;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.dto.LoginMinRequestDto;
|
||||||
|
import com.xit.biz.ctgy.auth.service.IAuthMinService;
|
||||||
|
import com.xit.core.api.IRestResponse;
|
||||||
|
import com.xit.core.api.RestResponse;
|
||||||
|
import com.xit.core.oauth2.api.dto.TokenRequestDto;
|
||||||
|
import com.xit.core.oauth2.oauth.JwtTokenProvider;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Tag(name = "OAuth2MinController", description = "인증 관리")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/ctgy/auth")
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OAuth2MinController {
|
||||||
|
|
||||||
|
|
||||||
|
private final IAuthMinService authMinService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* 로그인 요청을 처리(Redis에 저장)하고 토큰(Access + Refresh) return
|
||||||
|
*
|
||||||
|
* 1. Login ID, PW 로 인증 정보 객체 UsernamePasswordAuthenticationToken 생성
|
||||||
|
* 2. AuthenticationManager에 authenticate 메소드의 파라미터로 넘겨, 검증 후 Authentication(사용자ID가 들어있다) return
|
||||||
|
* AuthenticationManager --> 스프링 시큐리티의 실제 인증이 이루어지는 곳
|
||||||
|
* authenticate 메소드 하나만 정의되어 있는 인터페이스
|
||||||
|
* Builder 에서 UserDetails 의 유저 정보가 서로 일치하는지 검사
|
||||||
|
* 3. 인증정보로 JWT 토큰 생성
|
||||||
|
* 4. Refresh 토큰 저장 : : Redis에 저장
|
||||||
|
* 5. 토큰(Access + Refresh) return
|
||||||
|
* </pre>
|
||||||
|
* @see AuthenticationManagerBuilder
|
||||||
|
* @see JwtTokenProvider
|
||||||
|
*
|
||||||
|
* @param loginRequestDto LoginMinRequestDto
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @param session Session
|
||||||
|
* @return ResponseEntity
|
||||||
|
*/
|
||||||
|
@Operation(summary = "login" , description = "login")
|
||||||
|
// @Parameters({
|
||||||
|
// @Parameter(in = ParameterIn.QUERY, name = "providerType", description = "ProviderType", required = true, example = "GOOGLE"),
|
||||||
|
// @Parameter(in = ParameterIn.QUERY, name = "userId", description = "사용자ID", required = true, example = "minuk926926"),
|
||||||
|
// @Parameter(in = ParameterIn.QUERY, name = "password", description = "비밀번호", required = true, example = "minuk926926")
|
||||||
|
// })
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseEntity<? extends IRestResponse> login(
|
||||||
|
//@Validated
|
||||||
|
@Valid
|
||||||
|
@RequestBody
|
||||||
|
final LoginMinRequestDto loginRequestDto,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
HttpSession session
|
||||||
|
//Errors errors
|
||||||
|
|
||||||
|
) {
|
||||||
|
|
||||||
|
return RestResponse.of(
|
||||||
|
authMinService.login(
|
||||||
|
loginRequestDto,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
session
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* JWT token 재발급
|
||||||
|
* access token - header, refresh - TokenRequestDto 에 담아 요청
|
||||||
|
*
|
||||||
|
* 1. access token의 유효기간이 남아 있는 경우 ErrorCode.NOT_EXPIRED_TOKEN_YET
|
||||||
|
* 2. refresh token의 유효기간이 재 발급 기준에 부합된 경우 refresh token도 재발급
|
||||||
|
* 3. 2번 항묵에 미 해당시 refresh token 값은 null로
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param tokenRequestDto TokenRequestDto
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @return ResponseEntity IRestResponse
|
||||||
|
*/
|
||||||
|
@Operation(summary = "token 재발급 요청(header & DTO 사용)" , description = "token 재발급 :: accessToken - header, refreshToken - dto")
|
||||||
|
@Parameter(in = ParameterIn.QUERY, name = "refreshToken", description = "refresh token", required = true, example = " ")
|
||||||
|
@PostMapping("/jwt/reissue")
|
||||||
|
public ResponseEntity<? extends IRestResponse> reissueFromHeader(
|
||||||
|
@Parameter(hidden = true)
|
||||||
|
@NotNull
|
||||||
|
@RequestBody
|
||||||
|
final TokenRequestDto tokenRequestDto,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return RestResponse.of(authMinService.reissue(tokenRequestDto, request, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* JWT token 재발급
|
||||||
|
* access token - header, refresh - cookie 에 담아 요청
|
||||||
|
*
|
||||||
|
* 1. access token의 유효기간이 남아 있는 경우 ErrorCode.NOT_EXPIRED_TOKEN_YET
|
||||||
|
* 2. refresh token의 유효기간이 재 발급 기준에 부합된 경우 refresh token도 재발급
|
||||||
|
* 3. 2번 항묵에 미 해당시 refresh token 값은 null로
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @return ResponseEntity IRestResponse
|
||||||
|
*/
|
||||||
|
@Operation(summary = "token 재발급(header & cookie 사용)" , description = "토큰 재발급 :: accessToken - header, refreshToken - cookie")
|
||||||
|
//@Parameter(in = ParameterIn.HEADER, name = "Authorization", description = "access token", required = true, example = " ")
|
||||||
|
//@Parameter(in = ParameterIn.COOKIE, name = "refreshToken", description = "refresh token", required = true, example = " ")
|
||||||
|
@PostMapping("/jwt/reissue/cookie")
|
||||||
|
public ResponseEntity<? extends IRestResponse> reissueFromCookie(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
return RestResponse.of(authMinService.reissue(null, request, response));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* JWT token 재발급
|
||||||
|
* access token - TokenRequestDto, refresh - TokenRequestDto 에 담아 요청
|
||||||
|
*
|
||||||
|
* 1. access token의 유효기간이 남아 있는 경우 ErrorCode.NOT_EXPIRED_TOKEN_YET
|
||||||
|
* 2. refresh token의 유효기간이 재 발급 기준에 부합된 경우 refresh token도 재발급
|
||||||
|
* 3. 2번 항묵에 미 해당시 refresh token 값은 null로
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param tokenRequestDto TokenRequestDto
|
||||||
|
* @return ResponseEntity IRestResponse
|
||||||
|
*/
|
||||||
|
@Operation(summary = "token 재발급(DTO 사용)" , description = "token 재발급 :: accessToken - dto, refreshToken - dto")
|
||||||
|
@PostMapping("/jwt/reissue/dto")
|
||||||
|
public ResponseEntity<? extends IRestResponse> reissueFromParam(
|
||||||
|
@NotNull
|
||||||
|
@RequestBody
|
||||||
|
final TokenRequestDto tokenRequestDto) {
|
||||||
|
return RestResponse.of(authMinService.reissue(tokenRequestDto, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "토큰 체크" , description = "access token 체크")
|
||||||
|
@PostMapping("/jwt/validate")
|
||||||
|
public ResponseEntity<? extends IRestResponse> validate(@RequestParam final String accessToken, @RequestParam boolean isExceptionThrow) {
|
||||||
|
return RestResponse.of(authMinService.validationToken(accessToken, isExceptionThrow));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "토큰 정보 확인" , description = "토큰 정보 확인")
|
||||||
|
@PostMapping("/jwt/info")
|
||||||
|
public ResponseEntity<? extends IRestResponse> findAccessTokenInfo(@RequestParam final String accessToken) {
|
||||||
|
return RestResponse.of(authMinService.findAccessTokenInfo(accessToken));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.xit.biz.ctgy.dto;
|
||||||
|
|
||||||
|
import com.xit.biz.cmm.entity.CmmUser;
|
||||||
|
import com.xit.core.oauth2.oauth.entity.ProviderType;
|
||||||
|
import com.xit.core.oauth2.oauth.entity.RoleType;
|
||||||
|
import com.xit.core.support.valid.Enums;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validation (@NotBlank) class -> hibernate class 사용
|
||||||
|
*/
|
||||||
|
@Schema(name = "LoginRequestDto", description = "로그인 parameter DTO")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class LoginMinRequestDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum type Validation -> controller에서 처리
|
||||||
|
*/
|
||||||
|
@Schema(required = true, title = "Provider Type", example = "LOCAL", description = "Provider TYPE")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Enums(enumClass = ProviderType.class, ignoreCase = false, message = "{auth.user.pattern.ProviderType}")
|
||||||
|
private ProviderType providerType;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "사용자ID", example = "xitdev", description = "사용자 ID")
|
||||||
|
@Pattern(regexp = "[0-9a-zA-z]{6,20}", message = "{auth.user.pattern.id}")
|
||||||
|
@Size(min = 6, max = 20)
|
||||||
|
private String userid;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "비밀번호", example = "xitdev", description = "비밀 번호")
|
||||||
|
@NotNull//(message = "비밀번호는 필수 입니다(6 ~ 20자리)")
|
||||||
|
@Pattern(regexp = "[0-9a-zA-z!@#$%^&*]{6,20}", message = "{auth.user.pattern.password}")
|
||||||
|
@Size(min = 6, max = 20)
|
||||||
|
private String passwd;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public CmmUser toUser(PasswordEncoder passwordEncoder) {
|
||||||
|
return CmmUser.builder()
|
||||||
|
.providerType(providerType)
|
||||||
|
//.providerType(ProviderType.from(providerType))
|
||||||
|
.userId(userid)
|
||||||
|
.password(passwordEncoder.encode(passwd))
|
||||||
|
.roleType(RoleType.USER)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsernamePasswordAuthenticationToken toAuthentication() {
|
||||||
|
return new UsernamePasswordAuthenticationToken(userid, passwd);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.xit.biz.ctgy.dto;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.entity.MinSimsaUser680;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Schema(name = "MinUserinfoDto", description = "사용자 DTO")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MinUserinfoDto implements Serializable {
|
||||||
|
private static final long SerialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "민원심사사용자코드", example = " ", description = "Input Description...")
|
||||||
|
private String userid;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "사용자암호", example = " ", description = "Input Description...")
|
||||||
|
private String passwd;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "사용자성명", example = " ", description = "Input Description...")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(required = false, title = "사용자주민번호", example = " ", description = "Input Description...")
|
||||||
|
private String regnum;
|
||||||
|
|
||||||
|
@Schema(required = false, title = "전화번호", example = " ", description = "Input Description...")
|
||||||
|
private String mphone;
|
||||||
|
|
||||||
|
@Schema(required = false, title = "이메일", example = " ", description = "Input Description...")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "사용구분", example = " ", description = "Input Description...")
|
||||||
|
private String accesstype;
|
||||||
|
|
||||||
|
@Schema(required = true, title = "단속조", example = " ", description = "Input Description...")
|
||||||
|
private String team;
|
||||||
|
|
||||||
|
@Schema(required = false, title = "등록일시", example = " ", description = "Input Description...")
|
||||||
|
private java.sql.Date regdate;
|
||||||
|
|
||||||
|
@Schema(required = false, title = "사용여부", example = " ", description = "Input Description...")
|
||||||
|
private String isenable;
|
||||||
|
|
||||||
|
@Schema(required = false, title = "구청코드", example = " ", description = "Input Description...")
|
||||||
|
private String gu;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
MinUserinfoDto that = (MinUserinfoDto) o;
|
||||||
|
return Objects.equals(userid, that.userid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(userid);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.xit.biz.ctgy.dto.struct;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.dto.MinUserinfoDto;
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import com.xit.core.support.jpa.mapstruct.IMapstruct;
|
||||||
|
import com.xit.core.support.jpa.mapstruct.MapStructMapperConfig;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
|
@Mapper(config = MapStructMapperConfig.class)
|
||||||
|
public interface MinUserinfoMapstruct extends IMapstruct<MinUserinfoDto, MinUserinfo> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.xit.biz.ctgy.repository;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface IMinUserinfoRepository extends JpaRepository<MinUserinfo, String> {
|
||||||
|
Optional<MinUserinfo> findByUserid(String userid);
|
||||||
|
|
||||||
|
@Query(value = "SELECT TRAFFIC.ECL_ENCRYPT(?1) AS passwd FROM DUAL C", nativeQuery = true)
|
||||||
|
String queryGetPasswdEncode(@Param("passwd") String passwd);
|
||||||
|
|
||||||
|
@Query(value = "SELECT TRAFFIC.ECL_DECRYPT(MU.passwd) AS passwd FROM min_userinfo MU WHERE MU.userid = ?1", nativeQuery = true)
|
||||||
|
String queryGetPasswd(@Param("userid") String userid);
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.xit.biz.ctgy.service;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
public interface IMinUserinfoService {
|
||||||
|
|
||||||
|
Page<MinUserinfo> findMinUserinfos(MinUserinfo minUserinfo, Pageable pageable);
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.xit.biz.ctgy.service.impl;
|
||||||
|
|
||||||
|
import com.xit.biz.ctgy.entity.MinUserinfo;
|
||||||
|
import com.xit.biz.ctgy.repository.IMinUserinfoRepository;
|
||||||
|
import com.xit.biz.ctgy.service.IMinUserinfoService;
|
||||||
|
import com.xit.core.support.jpa.JpaUtil;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.ExampleMatcher;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.contains;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MinUserinfoService implements IMinUserinfoService {
|
||||||
|
|
||||||
|
private final IMinUserinfoRepository repository;
|
||||||
|
|
||||||
|
public MinUserinfoService(IMinUserinfoRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional//(readOnly = true)
|
||||||
|
public Page<MinUserinfo> findMinUserinfos(MinUserinfo minUserinfo, Pageable pageable) {
|
||||||
|
//Sort sort = Sort.by(Sort.Direction.ASC, "codeOrdr");
|
||||||
|
pageable = JpaUtil.getPagingInfo(pageable);
|
||||||
|
ExampleMatcher exampleMatcher = ExampleMatcher.matchingAll()
|
||||||
|
.withMatcher("userid", contains())
|
||||||
|
.withMatcher("name", contains());
|
||||||
|
Example<MinUserinfo> example = Example.of(minUserinfo, exampleMatcher);
|
||||||
|
Page<MinUserinfo> page = repository.findAll(example, pageable);
|
||||||
|
// List<CmmUser> userList = page.getContent();
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue