feat: mens-admin 토큰 인증 feat

dev
gitea-관리자 12 months ago
parent 3ca0b7eae6
commit 5f792f10e7

@ -61,6 +61,10 @@
<scope>system</scope>
<systemPath>${basedir}/lib/ojdbc6.jar</systemPath>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>

@ -1,116 +0,0 @@
package egovframework.com.jwt;
import egovframework.com.cmm.LoginVO;
import egovframework.com.cmm.util.EgovStringUtil;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import java.io.IOException;
import java.util.Arrays;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import kr.xit.core.consts.Constants.JwtToken;
import kr.xit.core.spring.util.MessageUtil;
import kr.xit.core.support.utils.Checks;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* fileName : JwtAuthenticationFilter
* author : crlee
* date : 2023/06/11
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023/06/11 crlee
*/
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private EgovJwtTokenUtil jwtTokenUtil;
@Autowired
private MessageUtil messageUtil;
@Autowired
private JwtVerification verification;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
boolean verificationFlag = true;
// step 1. request header에서 토큰을 가져온다.
String jwtToken = EgovStringUtil.isNullToString(request.getHeader(JwtToken.HEADER_NAME.getCode()));
if(Checks.isEmpty(jwtToken)){
setError(response, jwtToken, messageUtil.getMessage("fail.auth.header.invalid", new String[]{jwtToken}));
chain.doFilter(request, response);
return;
}
// token validation
if(!verification.isVerification(jwtToken)){
setError(response, jwtToken, messageUtil.getMessage("fail.auth.header.invalid", new String[]{jwtToken}));
chain.doFilter(request, response);
return;
};
// step 2. 토큰에 내용이 있는지 확인해서 id값을 가져옴
// Exception 핸들링 추가처리 (토큰 유효성, 토큰 변조 여부, 토큰 만료여부)
// 내부적으로 parse하는 과정에서 해당 여부들이 검증됨
String id = null;
try {
id = jwtTokenUtil.getUserIdFromToken(jwtToken);
if (id == null) {
logger.debug("jwtToken not validate");
verificationFlag = false;
}
logger.debug("===>>> id = " + id);
} catch (IllegalArgumentException | ExpiredJwtException | MalformedJwtException | UnsupportedJwtException | SignatureException e) {
setError(response, jwtToken, "Unable to verify JWT Token: " + e.getMessage());
verificationFlag = false;
}
LoginVO loginVO = new LoginVO();
if( verificationFlag ){
logger.debug("jwtToken validated");
loginVO.setId(id);
loginVO.setUserSe( jwtTokenUtil.getUserSeFromToken(jwtToken) );
loginVO.setUniqId( jwtTokenUtil.getInfoFromToken("uniqId",jwtToken) );
loginVO.setOrgnztId( jwtTokenUtil.getInfoFromToken("orgnztId",jwtToken) );
loginVO.setName( jwtTokenUtil.getInfoFromToken("name",jwtToken) );
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginVO, null,
Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection", "unchecked"})
private void setError(HttpServletResponse response, final String jwtToken, final String errMsg) throws IOException {
log.error(errMsg);
// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
// JSONObject resJson = new JSONObject();
// resJson.put("code", 401);
// resJson.put("message", errMsg);
// response.getWriter().write(resJson.toJSONString());
}
}

@ -1,90 +0,0 @@
package egovframework.com.security;
import egovframework.com.jwt.JwtAuthenticationEntryPoint;
import egovframework.com.jwt.JwtAuthenticationFilter;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* fileName : SecurityConfig
* author : crlee
* date : 2023/06/10
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023/06/10 crlee
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig implements WebMvcConfigurer {
//Http Methpd : Get 인증예외 List
private String[] AUTH_GET_WHITELIST = {
"/schedule/daily", //일별 일정 조회
"/schedule/week", //주간 일정 조회
"/schedule/{schdulId}", //일정 상세조회
};
// 인증 예외 List
@Value("${app.spring.security.white-list}")
private String[] AUTH_WHITELIST;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new CustomAuthenticationPrincipalResolver());
}
@Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationFilter();
}
// @Bean
// protected CorsConfigurationSource corsConfigurationSource() {
// CorsConfiguration configuration = new CorsConfiguration();
//
// configuration.setAllowedOriginPatterns(Arrays.asList("*"));
// configuration.setAllowedMethods(Arrays.asList("HEAD","POST","GET","DELETE","PUT"));
// configuration.setAllowedOrigins(Arrays.asList(ORIGINS_WHITELIST));
// configuration.setAllowedHeaders(Arrays.asList("*"));
// configuration.setAllowCredentials(true);
//
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// source.registerCorsConfiguration("/**", configuration);
// return source;
// }
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers(HttpMethod.GET,AUTH_GET_WHITELIST).permitAll()
.anyRequest().authenticated()
).sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.cors().and()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandlingConfigurer ->
exceptionHandlingConfigurer
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
)
.build();
}
}

@ -1,27 +0,0 @@
package kr.xit.biz.auth.mapper;
import egovframework.com.cmm.LoginVO;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.auth.mapper
* fileName : IAuthApiMapper
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
@Mapper
public interface IAuthApiMapper {
LoginVO actionLogin(LoginVO vo);
// LoginVO searchId(LoginVO vo);
// LoginVO searchPassword(LoginVO vo);
// void updatePassword(LoginVO vo);
}

@ -1,17 +1,17 @@
package kr.xit.biz.auth.service;
import egovframework.com.cmm.LoginVO;
import egovframework.com.cmm.model.LoginVO;
import egovframework.com.cmm.util.EgovFileScrty;
import javax.annotation.Resource;
import kr.xit.biz.auth.mapper.IAuthApiMapper;
import kr.xit.core.spring.auth.mapper.IAuthMapper;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class AuthApiService extends EgovAbstractServiceImpl implements IAuthApiService {
public class AuthService extends EgovAbstractServiceImpl implements IAuthService {
@Resource
private IAuthApiMapper mapper;
private IAuthMapper mapper;
/**
*
@ -26,7 +26,7 @@ public class AuthApiService extends EgovAbstractServiceImpl implements IAuthApiS
vo.setPassword(enpassword);
// 2. 아이디와 암호화된 비밀번호가 DB와 일치하는지 확인한다.
LoginVO loginVO = mapper.actionLogin(vo); //loginDAO.actionLogin(vo);
LoginVO loginVO = mapper.login(vo); //loginDAO.actionLogin(vo);
// 3. 결과를 리턴한다.
if (loginVO != null && !loginVO.getId().equals("") && !loginVO.getPassword().equals("")) {

@ -1,6 +1,6 @@
package kr.xit.biz.auth.service;
import egovframework.com.cmm.LoginVO;
import egovframework.com.cmm.model.LoginVO;
/**
*
@ -19,7 +19,7 @@ import egovframework.com.cmm.LoginVO;
*
* </pre>
*/
public interface IAuthApiService {
public interface IAuthService {
/**
*

@ -1,7 +1,6 @@
package kr.xit.biz.auth.web;
import egovframework.com.cmm.LoginVO;
import egovframework.com.jwt.EgovJwtTokenUtil;
import egovframework.com.cmm.model.LoginVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
@ -9,10 +8,12 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import kr.xit.biz.auth.service.IAuthApiService;
import kr.xit.biz.auth.service.IAuthService;
import kr.xit.core.consts.Constants;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.model.IApiResponse;
import kr.xit.core.model.TokenDTO;
import kr.xit.core.spring.auth.jwt.JwtTokenProvider;
import kr.xit.core.spring.util.MessageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
@ -45,18 +46,18 @@ import org.springframework.web.context.request.RequestContextHolder;
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/biz/auth")
public class AuthApiController {
public class AuthController {
@Value("${app.token.saveType:header}")
private String authSaveType;
/** EgovLoginService */
private final IAuthApiService loginService;
private final IAuthService loginService;
/** EgovMessageSource */
private final MessageUtil messageUtil;
private final EgovJwtTokenUtil egovJwtTokenUtil;
private final JwtTokenProvider jwtTokenProvider;
/**
*
@ -136,10 +137,11 @@ public class AuthApiController {
Map<String, Object> claimsMap = new HashMap<>();
// claimsMap.put("dkkdk", "kdkkdkdkd");
String jwtToken = egovJwtTokenUtil.generateToken(loginVO, claimsMap);
// String jwtToken = egovJwtTokenUtil.generateToken(loginVO.getId());
TokenDTO tokenDTO = jwtTokenProvider.generateTokenDto(claimsMap, String.format("{}{}",loginVO.getUserSe(), loginVO.getId()));
String username = egovJwtTokenUtil.getUsernameFromToken(jwtToken);
// String jwtToken = egovJwtTokenUtil.generateToken(loginVO.getId());
String username = jwtTokenProvider.getUsernameFromToken(tokenDTO.getAccessToken());
// System.out.println("Dec jwtToken username = "+username);
@ -172,7 +174,7 @@ public class AuthApiController {
//String jwtToken = jwtTokenProvider.generateJwtAccessToken(loginVO.getId(), "ROLE_USER");
resultMap.put("resultVO", loginResultVO);
resultMap.put("token", jwtToken);
resultMap.put("token", tokenDTO);
return ApiResponseDTO.success(resultMap);
}

@ -1,4 +1,4 @@
package egovframework.com.jwt;
package kr.xit.core.spring.auth;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
@ -22,7 +22,7 @@ import org.springframework.security.web.AuthenticationEntryPoint;
*/
//@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override

@ -1,6 +1,7 @@
package egovframework.com.security;
package kr.xit.core.spring.auth;
import egovframework.com.cmm.LoginVO;
import egovframework.com.cmm.model.LoginVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@ -20,6 +21,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
* -----------------------------------------------------------
* 2023/07/13 crlee
*/
@Slf4j
public class CustomAuthenticationPrincipalResolver implements HandlerMethodArgumentResolver {
@Override
@ -37,9 +39,10 @@ public class CustomAuthenticationPrincipalResolver implements HandlerMethodArgum
authentication.getPrincipal() == null ||
"anonymousUser".equals(authentication.getPrincipal())
) {
log.info("익명(anonymousUser) 사용자 로그인 처리로 LoginVO 생성");
return new LoginVO();
}
log.info("사용자 로그인 {}", authentication.getPrincipal());
return authentication.getPrincipal();
}
}

@ -0,0 +1,49 @@
package kr.xit.core.spring.auth;
import java.util.List;
import kr.xit.core.exception.BizRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
/**
* <pre>
* description : utilis class
* packageName : kr.xit.core.spring.auth
* fileName : CustomAuthorityUtils
* author : julim
* date : 2023-11-29
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-11-29 julim
*
* </pre>
*/
@Slf4j
public class CustomAuthorityUtils {
/**
* <pre>
* role List<GrantedAuthority>
* ROLE_USER, ROLE_ADMIN
* @param role : ROLE_USR|ROLE_ADMIN
* @return List<GrantedAuthority> </pre>
*/
public static List<GrantedAuthority> createAuthorities(String role) {
return List.of(new SimpleGrantedAuthority("ROLE_" + role));
}
/**
* role
* @param role String USER|ADMIN
*/
public static void verifiedRole(String role) {
if (role == null) {
throw BizRuntimeException.create("fail.jwt.role.notExists");
} else if (!role.equals("USER") && !role.equals("ADMIN")) {
throw BizRuntimeException.create("fail.jwt.role.invalid", new String[]{role});
}
}
}

@ -0,0 +1,116 @@
package kr.xit.core.spring.auth;
import egovframework.com.cmm.model.LoginVO;
import java.util.Collection;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* <pre>
* description : UserDetails
* Spring Security User
* packageName : kr.xit.core.spring.auth
* fileName : CustomUserDetails
* author : julim
* date : 2023-11-29
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-11-29 julim
*
* </pre>
*/
@Getter
@NoArgsConstructor
@ToString
public class CustomUserDetails implements UserDetails {
private String id;
private String email;
private String role;
private String password;
private CustomUserDetails(LoginVO loginVO) {
this.id = loginVO.getId();
this.email = loginVO.getEmail();
this.password = loginVO.getPassword();
//this.role = loginVO.getRole();
}
private CustomUserDetails(String email, String role) {
this.email = email;
this.role = role;
}
private CustomUserDetails(String email, String password, String role) {
this.email = email;
this.password = password;
this.role = role;
}
public static CustomUserDetails of(LoginVO loginVO) {
return new CustomUserDetails(loginVO);
}
public static CustomUserDetails of(String email, String role) {
return new CustomUserDetails(email, role);
}
public static CustomUserDetails of(String email, String password, String role) {
return new CustomUserDetails(email, password, role);
}
/**
* <pre>
* List<GrantedAuthority>
* @return Collection<? extends GrantedAuthority>
* </pre>
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return CustomAuthorityUtils.createAuthorities(role);
}
@Override
public String getUsername() {
return this.id;
}
/**
* ?
* @return true|false
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* ?
* @return true| false
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* ?
* @return true| flase
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* ?
* @return true|false
*/
@Override
public boolean isEnabled() {
return true;
}
}

@ -0,0 +1,154 @@
package kr.xit.core.spring.auth;
import java.util.List;
import kr.xit.core.spring.auth.filter.JwtAuthenticationFilter;
import kr.xit.core.spring.auth.handler.CustomAccessDeniedHandler;
import kr.xit.core.spring.auth.handler.CustomAuthenticationFailureHandler;
import kr.xit.core.spring.auth.handler.CustomAuthenticationSuccessHandler;
import kr.xit.core.spring.auth.jwt.JwtTokenProvider;
import kr.xit.core.spring.auth.mapper.IAuthMapper;
import kr.xit.core.spring.util.AES128Config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* <pre>
* description : Spring security
* packageName : kr.xit.core.spring.auth
* fileName : SecurityConfig
* author : julim
* date : 2023-11-29
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-11-29 julim
*
* </pre>
*/
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig implements WebMvcConfigurer {
//Http Methpd : Get 인증예외 List
private String[] AUTH_GET_WHITELIST = {
"/schedule/daily", //일별 일정 조회
"/schedule/week", //주간 일정 조회
"/schedule/{schdulId}", //일정 상세조회
};
// 인증 예외 List
@Value("${app.spring.security.white-list}")
private String[] AUTH_WHITELIST;
private final JwtTokenProvider jwtTokenProvider;
private final AES128Config aes128Config;
private final IAuthMapper authMapper;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new CustomAuthenticationPrincipalResolver());
}
/**
* <pre>
* SecurityFilterChain
* ->
* <code>headers().frameOptions().sameOrigin()</code> : X-Frame-Options SAMEORIGIN , iframe
* <code>http.csrf().disable()</code> : jwt CSRF(Cross-Site Request Forgery) .
* <code>http.cors().configurationSource(corsConfigurationSource())</code> : CORS(Cross-Origin Resource Sharing) -
* <code>http.formLogin().disable()</code> :
* <code>http.httpBasic().disable()</code> : HTTP
* <code>sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)</code> :
* <code>exceptionHandling()</code> :
* <code>authenticationEntryPoint(new CustomAuthenticationEntryPoint())</code> :
* <code>accessDeniedHandler(new CustomAccessDeniedHandler())</code> :
* <code>apply(new CustomFilterConfigurer())</code> :
* <code>authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll())</code> : HTTP
* @param http HttpSecurity
* @return SecurityFilterChain
* </pre>
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers().frameOptions().sameOrigin()
.and()
.csrf().disable()
//.cors().configurationSource(corsConfigurationSource())
.cors().and()
.formLogin().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.accessDeniedHandler(new CustomAccessDeniedHandler())
.and()
.apply(new CustomFilterConfigurer(authMapper))
.and()
// TODO: 추후 권한 별 페이지 접근제어 설정 예정
.authorizeHttpRequests(authorize -> authorize
.antMatchers(AUTH_WHITELIST).permitAll()
//.antMatchers(AUTH_WHITELIST).authenticated()
.antMatchers(HttpMethod.GET,AUTH_GET_WHITELIST).permitAll()
.anyRequest().authenticated()
);
return http.build();
}
/**
* <pre>
* description : HttpSecurity
* ->
* - AbstractHttpConfigurer configure()
* packageName : kr.xit.core.spring.auth
* fileName : CustomFilterConfigurer
* author : julim
* date : 2023-11-29
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-11-29 julim
*
* </pre>
*/
@RequiredArgsConstructor
public class CustomFilterConfigurer extends
AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
private final IAuthMapper authMapper;
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
log.info("SecurityConfiguration.CustomFilterConfigurer.configure excute");
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(
AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager,
jwtTokenProvider, aes128Config, authMapper);
//JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenProvider, redisService);
jwtAuthenticationFilter.setFilterProcessesUrl("/auth/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
httpSecurity
.addFilter(jwtAuthenticationFilter)
// .addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
// .addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
;
}
}
}

@ -0,0 +1,103 @@
package kr.xit.core.spring.auth.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import egovframework.com.cmm.model.LoginVO;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.model.RefreshTokenDTO;
import kr.xit.core.model.TokenDTO;
import kr.xit.core.spring.auth.CustomUserDetails;
import kr.xit.core.spring.auth.jwt.JwtTokenProvider;
import kr.xit.core.spring.auth.mapper.IAuthMapper;
import kr.xit.core.spring.util.AES128Config;
import kr.xit.core.support.utils.DateUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* <pre>
* description : /
* UsernamePasswordAuthenticationFilter
* 1. .
* -> successfulAuthentication/unSuccessfulAuthentication
* 2. /login
* -> Url setFilterProcessesUrl() Url
* 3. Jwt .
* packageName : kr.xit.core.spring.auth.filter
* fileName : JwtAuthorizationFilter
* author : julim
* date : 2023-11-29
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-11-29 julim
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
private final AES128Config aes128Config;
private final IAuthMapper authMapper;
@SneakyThrows
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// ServletInputStream을 LoginDto 객체로 역직렬화
ObjectMapper objectMapper = new ObjectMapper();
LoginVO loginVO = objectMapper.readValue(request.getInputStream(), LoginVO.class);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginVO.getEmail(), loginVO.getPassword());
return authenticationManager.authenticate(authenticationToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
Map<String,Object> cliams = new HashMap<>();
cliams.put("role", customUserDetails.getRole());
TokenDTO tokenDto = jwtTokenProvider.generateTokenDto(cliams, customUserDetails.getUsername());
String accessToken = tokenDto.getAccessToken();
String refreshToken = tokenDto.getRefreshToken();
String encryptedRefreshToken = aes128Config.encryptAes(refreshToken);
jwtTokenProvider.accessTokenSetHeader(accessToken, response);
jwtTokenProvider.refresshTokenSetHeader(encryptedRefreshToken, response);
LoginVO loginVO = authMapper.selectUserById(customUserDetails.getId())
.orElseThrow(() -> BizRuntimeException.of("fail.auth.login.user"));
//TODO:: 로그인 성공시 Refresh Token 저장
Date date = jwtTokenProvider.getExpirationDateFromToken(refreshToken);
authMapper.saveRefreshToken(
RefreshTokenDTO.builder()
.id(loginVO.getId())
.refreshToken(refreshToken)
.refreshTokenExpiresIn(DateUtils.parseToLong(date))
.build()
);
//...(loginVO.getId(), refreshToken, Duration.ofMillis(DateUtils.parseToLong(date)));
this.getSuccessHandler().onAuthenticationSuccess(request, response, authentication);
}
}

@ -0,0 +1,24 @@
package kr.xit.core.spring.auth.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private String denied_url;
public CustomAccessDeniedHandler() {
this.denied_url = "egovframework/com/cmm/error/csrfAccessDenied";
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect(request.getContextPath() + denied_url);
}
}

@ -0,0 +1,32 @@
package kr.xit.core.spring.auth.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException, IOException {
String errorMessage = "Invalid Username or Password";
if(exception instanceof BadCredentialsException){
errorMessage = "Invalid Username or Password";
}else if(exception instanceof InsufficientAuthenticationException){
errorMessage = "Invalid Secret Key";
}
setDefaultFailureUrl("/auth/login?error=true&exception=" + exception.getMessage());
super.onAuthenticationFailure(request, response, exception);
}
}

@ -0,0 +1,36 @@
package kr.xit.core.spring.auth.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final RequestCache requestCache = new HttpSessionRequestCache();
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
setDefaultTargetUrl("/auth/login");
SavedRequest savedRequest = requestCache.getRequest(request, response);
// 사용자가 권한이 필요한 자원에 접근해 인증 예외가 발생해 인증을 처리하는 것이 아닌 경우
// SavedRequest 객체가 생성되지 않는다.
if (savedRequest != null) {
String targetUrl = savedRequest.getRedirectUrl();
redirectStrategy.sendRedirect(request, response, targetUrl);
} else {
redirectStrategy.sendRedirect(request, response, getDefaultTargetUrl());
}
super.clearAuthenticationAttributes(request);
}
}

@ -0,0 +1,29 @@
package kr.xit.core.spring.auth.mapper;
import egovframework.com.cmm.model.LoginVO;
import java.util.Optional;
import kr.xit.core.model.RefreshTokenDTO;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.spring.auth.mapper
* fileName : IAuthMapper
* author : limju
* date : 2023-11-29
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-11-29 limju
*
* </pre>
*/
@Mapper
public interface IAuthMapper {
<T> LoginVO login(T t);
Optional<LoginVO> selectUserById(String id);
int saveRefreshToken(final RefreshTokenDTO dto);
}

@ -0,0 +1,32 @@
package kr.xit.core.spring.auth.service;
import egovframework.com.cmm.model.LoginVO;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.spring.auth.CustomUserDetails;
import kr.xit.core.spring.auth.mapper.IAuthMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final IAuthMapper mapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return mapper.selectUserById(username)
.map(this::createUserDetails)
.orElseThrow(() -> BizRuntimeException.of("fail.auth.login.user"));
}
private UserDetails createUserDetails(LoginVO member) {
return CustomUserDetails.of(member);
}
}

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.xit.biz.auth.mapper.IAuthApiMapper">
<!-- 일반 로그인 -->
<select id="actionLogin" resultType="egovframework.com.cmm.LoginVO">
<if test="userSe = 'USR'">
/** auth-mysql-mapper|actionLogin-로그인|julim */
SELECT user_id AS id
, user_nm AS name
, password
, ihidnum
, email_adres AS email
, 'USR' AS userSe
, orgnzt_id
, esntl_id
FROM xit_user_info
WHERE user_id = #{id}
AND password = #{password}
AND user_sttus_code = 'P'
</if>
</select>
</mapper>

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.xit.core.spring.auth.mapper.IAuthMapper">
<!-- 일반 로그인 -->
<select id="login" resultType="egovframework.com.cmm.model.LoginVO">
<if test="userSe = 'USR'">
/** auth-mysql-mapper|login-로그인|julim */
SELECT user_id AS id
, user_nm AS name
, password
, ihidnum
, email_adres AS email
, 'USR' AS userSe
, orgnzt_id
, esntl_id
FROM xit_user_info
WHERE user_id = #{id}
AND password = #{password}
AND user_sttus_code = 'P'
</if>
</select>
<select id="selectUserById" resultType="egovframework.com.cmm.model.LoginVO">
<if test="userSe = 'USR'">
/** auth-mysql-mapper|selectUserById-사용자조회|julim */
SELECT user_id AS id
, user_nm AS name
, password
, ihidnum
, email_adres AS email
, 'USR' AS userSe
, orgnzt_id
, esntl_id
FROM xit_user_info
WHERE user_id = #{id}
AND user_sttus_code = 'P'
</if>
</select>
<insert id="saveRefreshToken">
</insert>
</mapper>

@ -3,3 +3,5 @@
#------------------------------------------------
fail.auth.header.invalid=header\uAC00 \uC5C6\uAC70\uB098, \uD615\uC2DD\uC774 \uD2C0\uB9BD\uB2C8\uB2E4({0})
fail.auth.login.user=\uC0AC\uC6A9\uC790(\uD68C\uC6D0) \uC815\uBCF4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.

Loading…
Cancel
Save