login, logout 동작 추가.

cks
강석 최 2023-11-27 15:16:45 +09:00
parent 701047e22e
commit c5c3dd3850
7 changed files with 140 additions and 43 deletions

View File

@ -12,8 +12,7 @@ function EgovHeader({ loginUser, onChangeLogin }) {
console.log("[Start] EgovHeader ------------------------------"); console.log("[Start] EgovHeader ------------------------------");
const sessionUser = getSessionItem('loginUser'); const sessionUser = getSessionItem('loginUser');
const sessionUserId = sessionUser?.id; const sessionUserId = sessionUser?.userId;
const sessionUserName = sessionUser?.name;
const sessionUserSe = sessionUser?.userSe; const sessionUserSe = sessionUser?.userSe;
const navigate = useNavigate(); const navigate = useNavigate();
@ -85,7 +84,7 @@ function EgovHeader({ loginUser, onChangeLogin }) {
{/* 로그아웃 : 로그인 정보 있을때 */} {/* 로그아웃 : 로그인 정보 있을때 */}
{sessionUserId && {sessionUserId &&
<> <>
<span className="person">{sessionUserName} </span> 님이, 관리자로 로그인하셨습니다. <span className="person">{sessionUserId} </span> 님이, {sessionUserSe==='ADM'?'관리자':'사용자'}로그인하셨습니다.
<button onClick={logOutHandler} className="btn">로그아웃</button> <button onClick={logOutHandler} className="btn">로그아웃</button>
</> </>
} }
@ -161,7 +160,7 @@ function EgovHeader({ loginUser, onChangeLogin }) {
{/* 로그아웃 : 로그인 정보 있을때 */} {/* 로그아웃 : 로그인 정보 있을때 */}
{sessionUserId && {sessionUserId &&
<> <>
<span className="person">{sessionUserName} </span> 로그인하셨습니다. <span className="person">{sessionUserId} </span> 로그인하셨습니다.
<button onClick={logOutHandler} className="btn logout">로그아웃</button> <button onClick={logOutHandler} className="btn logout">로그아웃</button>
</> </>
} }

View File

@ -62,7 +62,7 @@ function EgovLoginContent(props) {
const submitFormHandler = (e) => { const submitFormHandler = (e) => {
console.log("EgovLoginContent submitFormHandler()"); console.log("EgovLoginContent submitFormHandler()");
const loginUrl = "/login" const loginUrl = "/auth/login"
const requestOptions = { const requestOptions = {
method: "POST", method: "POST",
headers: { headers: {
@ -74,7 +74,6 @@ function EgovLoginContent(props) {
EgovNet.requestFetch(loginUrl, EgovNet.requestFetch(loginUrl,
requestOptions, requestOptions,
(resp) => { (resp) => {
debugger
let resultVO = resp.resultVO; let resultVO = resp.resultVO;
let jToken = resp?.jToken || null; let jToken = resp?.jToken || null;
@ -108,28 +107,26 @@ function EgovLoginContent(props) {
<p className="txt">전자정부표준프레임워크 경량환경 홈페이지 로그인 페이지입니다.<br />로그인을 하시면 모든 서비스를 제한없이 이용하실 있습니다.</p> <p className="txt">전자정부표준프레임워크 경량환경 홈페이지 로그인 페이지입니다.<br />로그인을 하시면 모든 서비스를 제한없이 이용하실 있습니다.</p>
<div className="login_box"> <div className="login_box">
<form name="" method="" action="" onSubmit={submitFormHandler}> <fieldset>
<fieldset> <legend>로그인</legend>
<legend>로그인</legend> <span className="group">
<span className="group">
<input type="text" name="" title="아이디" placeholder="아이디" value={userInfo?.username} <input type="text" name="" title="아이디" placeholder="아이디" value={userInfo?.username}
onChange={e => setUserInfo({ ...userInfo, username: e.target.value })} /> onChange={e => setUserInfo({ ...userInfo, username: e.target.value })} />
<input type="password" name="" title="비밀번호" placeholder="비밀번호" <input type="password" name="" title="비밀번호" placeholder="비밀번호"
onChange={e => setUserInfo({ ...userInfo, password: e.target.value })} /> onChange={e => setUserInfo({ ...userInfo, password: e.target.value })} />
</span> </span>
<Row className="chk justify-content-between"> <Row className="chk justify-content-between">
<Col xs={3}> <Col xs={3}>
<label className="f_chk" htmlFor="saveid" ref={checkRef}> <label className="f_chk" htmlFor="saveid" ref={checkRef}>
<input type="checkbox" name="" id="saveid" onChange={handleSaveIDFlag} checked={saveIDFlag}/> <em>ID저장</em> <input type="checkbox" name="" id="saveid" onChange={handleSaveIDFlag} checked={saveIDFlag}/> <em>ID저장</em>
</label> </label>
</Col> </Col>
<Col xs={3}> <Col xs={3}>
<Link to={URL.JOIN}><em>회원가입</em></Link> <Link to={URL.JOIN}><em>회원가입</em></Link>
</Col> </Col>
</Row> </Row>
<button type="submit"><span>LOGIN</span></button> <button type="button" onClick={submitFormHandler}><span>LOGIN</span></button>
</fieldset> </fieldset>
</form>
</div> </div>
<ul className="list"> <ul className="list">

View File

@ -55,7 +55,7 @@ public class LoginVO implements Serializable{
@NotBlank(message = "비밀번호확인을 입력해주세요.") @NotBlank(message = "비밀번호확인을 입력해주세요.")
private String passwordChk; private String passwordChk;
@Schema(description = "사용자 구분", allowableValues = {"GNR", "ENT", "USR"}, defaultValue = "USR") @Schema(description = "사용자 구분", allowableValues = {"ADM", "USR"}, defaultValue = "USR")
private String userSe; private String userSe;
@Schema(description = "이름") @Schema(description = "이름")

View File

@ -0,0 +1,40 @@
package com.dbnt.kcscbackend.config.security;
import com.dbnt.kcscbackend.auth.entity.UserInfo;
import com.dbnt.kcscbackend.config.jwt.EgovJwtTokenUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Configuration
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
MediaType jsonMimeType = MediaType.APPLICATION_JSON;
HashMap<String, Object> resultMap = new HashMap<String, Object>();
resultMap.put("resultCode", "200");
if (jsonConverter.canWrite(resultMap.getClass(), jsonMimeType)) {
jsonConverter.write(resultMap, jsonMimeType, new ServletServerHttpResponse(response));
}
}
}

View File

@ -0,0 +1,69 @@
package com.dbnt.kcscbackend.config.security;
import com.dbnt.kcscbackend.auth.entity.UserInfo;
import com.dbnt.kcscbackend.config.jwt.EgovJwtTokenUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
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;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Configuration
public class CustomUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final EgovJwtTokenUtil jwtTokenUtil = new EgovJwtTokenUtil();
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest != null) {
requestCache.removeRequest(request, response);
clearAuthenticationAttributes(request);
}
UserInfo securityUser = null;
if (SecurityContextHolder.getContext().getAuthentication() != null) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
securityUser = (UserInfo) principal;
}
}
// application/json(ajax) 요청일 경우 아래의 처리!
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
MediaType jsonMimeType = MediaType.APPLICATION_JSON;
String jwtToken = jwtTokenUtil.generateToken(securityUser);
securityUser.setPassword(null);
securityUser.setUserSeq(null);
HashMap<String, Object> resultMap = new HashMap<String, Object>();
resultMap.put("resultCode", "200");
resultMap.put("resultVO", (UserDetails)securityUser);
resultMap.put("jToken", jwtToken);
// String userName = jwtTokenUtil.getUserSeFromToken(jwtToken);
response.addHeader("Authorization", "BEARER "+jwtToken);
if (jsonConverter.canWrite(resultMap.getClass(), jsonMimeType)) {
jsonConverter.write(resultMap, jsonMimeType, new ServletServerHttpResponse(response));
}
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}

View File

@ -22,7 +22,7 @@ import java.nio.charset.StandardCharsets;
@Slf4j @Slf4j
public class JsonUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public class JsonUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final String DEFAULT_LOGIN_REQUEST_URL = "/login"; // /login/oauth2/ + ????? 로 오는 요청을 처리할 것이다 private static final String DEFAULT_LOGIN_REQUEST_URL = "/auth/login"; // /login/oauth2/ + ????? 로 오는 요청을 처리할 것이다
private static final String HTTP_METHOD = "POST"; //HTTP 메서드의 방식은 POST 이다. private static final String HTTP_METHOD = "POST"; //HTTP 메서드의 방식은 POST 이다.
private static final String CONTENT_TYPE = "application/json";//json 타입의 데이터로만 로그인을 진행한다. private static final String CONTENT_TYPE = "application/json";//json 타입의 데이터로만 로그인을 진행한다.
private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER = private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER =

View File

@ -27,9 +27,11 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@ -69,7 +71,7 @@ public class SecurityConfig {
// 인증 예외 List // 인증 예외 List
private String[] AUTH_WHITELIST = { private String[] AUTH_WHITELIST = {
"/", "/",
"/login/**", "/auth/login",
"/login", "/login",
"/auth/join",//회원가입 "/auth/join",//회원가입
"/cmm/main/**.do", // 메인페이지 "/cmm/main/**.do", // 메인페이지
@ -128,28 +130,18 @@ public class SecurityConfig {
); );
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
http.cors().and().addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptionHandlingConfigurer -> .exceptionHandling(exceptionHandlingConfigurer ->
exceptionHandlingConfigurer exceptionHandlingConfigurer
.authenticationEntryPoint(new JwtAuthenticationEntryPoint()) .authenticationEntryPoint(new JwtAuthenticationEntryPoint())
); );
http.cors().and().addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/auth/logout")).logoutSuccessHandler(new CustomLogoutSuccessHandler());
return http.build(); return http.build();
} }
@Bean
public AuthenticationSuccessHandler loginSuccessHandler() {
return (request, response, authentication) -> {
UserInfo info = (UserInfo)authentication.getPrincipal();
String jwtToken = jwtTokenUtil.generateToken(info);
// String userName = jwtTokenUtil.getUserSeFromToken(jwtToken);
response.addHeader("Authorization", "BEARER "+jwtToken);
};
}
@Bean @Bean
public AuthenticationFailureHandler loginFailureHandler(){ public AuthenticationFailureHandler loginFailureHandler(){
return (request, response, exception) -> { return (request, response, exception) -> {
@ -159,7 +151,7 @@ public class SecurityConfig {
@Bean @Bean
public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() { public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() {
JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter(objectMapper, loginSuccessHandler(), loginFailureHandler()); JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter(objectMapper, new CustomUrlAuthenticationSuccessHandler(), loginFailureHandler());
jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager()); jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
return jsonUsernamePasswordAuthenticationFilter; return jsonUsernamePasswordAuthenticationFilter;
} }