반응형
1. 개요
- 전자정부프레임워크에서 제공하는 스프링 시큐리티 적용 두번째 글이다.
- https://arckwon.tistory.com/entry/%EC%A0%84%EC%9E%90%EC%A0%95%EB%B6%80%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%8A%A4%ED%94%84%EB%A7%81%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%A0%81%EC%9A%A912
- 앞에서는 시큐리티 사용하기위해 필요한 데이터 베이스및 기초작업을 하였다.
2. 개발환경 및 소스
- 개발환경은 egovframework 3.10 / jdk1.8 / tomcat8 / mariadb or mysql
- 공통컴포넌트에서 제공하는 기능을 웬만하면 바꾸지않고 최소한으로 하여 적용된 소스이다.
- 지금부터 변경해야 할부분 / 추가해야 할부분을 천천히 알아보자.
- 일부 파일의 경로가 기존과 다를수도있다. java 파일명은 그대로이기 때문에 package 경로는 본인들 원하는 위치로 하길 바란다.
- pom.xml 스프링시큐리티 관련 rte버전
- globals.properties
# 권한 인증방식(dummy, session, security) - 사용자의 로그인시 인증 방식을 결정함
Globals.Auth = security
- context-egovuserdetailshelper.xml
- 위 1,2번은 주석으로 처리하고 시큐리티(Security) 인증을 사용
<!-- 1.더미(Dummy) 인증
<beans profile="dummy">
<bean id="egovUserDetailsHelper" class="egovframework.com.cmm.util.EgovUserDetailsHelper"
p:egovUserDetailsService="#{new egovframework.com.cmm.service.impl.EgoDummyUserDetailsServiceImpl()}"/>
</beans>
-->
<!-- 2.세션(Session) 인증
<beans profile="session">
<bean id="egovUserDetailsHelper" class="egovframework.com.cmm.util.EgovUserDetailsHelper"
p:egovUserDetailsService="#{new egovframework.com.cmm.service.impl.EgovUserDetailsSessionServiceImpl()}"/>
</beans>
-->
<!-- 3.시큐리티(Security) 인증 -->
<beans profile="security">
<bean id="egovUserDetailsHelper" class="egovframework.com.cmm.helper.EgovUserDetailsHelper">
<property name="egovUserDetailsService" ref="egovUserDetailsSecurityService" />
</bean>
<!-- 스프링 시큐리티를 이용한 인증을 사용할 빈 -->
<bean id="egovUserDetailsSecurityService" class="egovframework.com.cmm.service.EgovUserDetailsSecurityServiceImpl"/>
</beans>
- context-security.xml
- 여기서 주의할점은 아래역할관리 관련 5개 SQL문은 필수적으로 있어야하고 쿼리 실행시 오류가 발생해도 안된다.
- 그리고 css html 같은 파일은 시큐리티 사용안함으로 한다.(security="none")
- loginUrl / logoutSuccessUrl을 잘기억해두자. 나중에 EgovWebApplicationInitializer.java 파일에서 매핑해야한다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:egov-security="http://maven.egovframe.go.kr/schema/egov-security"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd
http://maven.egovframe.go.kr/schema/egov-security http://maven.egovframe.go.kr/schema/egov-security/egov-security-3.10.0.xsd">
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/html/**" security="none"/>
<security:http pattern="/images/**" security="none"/>
<security:http pattern="/js/**" security="none"/>
<security:http pattern="/resource/**" security="none"/>
<security:http pattern="\A/WEB-INF/jsp/.*\Z" request-matcher="regex" security="none"/>
<egov-security:config id="securityConfig"
loginUrl="/login/index"
logoutSuccessUrl="/login/index"
loginFailureUrl="/login/index?login_error=1"
accessDeniedUrl="/common/access"
dataSource="egov.dataSource"
jdbcUsersByUsernameQuery="SELECT user_id
, user_id AS password
, 1 enabled
, user_nm
, email_adres
, mbtl_no
FROM cms.tb_user
WHERE user_id = ?"
jdbcAuthoritiesByUsernameQuery="SELECT a.scrty_dtrmn_trget_id as user_id, a.author_cd as authority
FROM cms.tb_cms_author_user a, cms.tb_user b
WHERE a.scrty_dtrmn_trget_id = b.user_id
AND b.user_id = ?"
jdbcMapClass="egovframework.com.sec.security.common.EgovSessionMapping"
requestMatcherType="regex"
hash="plaintext"
hashBase64="false"
concurrentMaxSessons="10"
concurrentExpiredUrl="/main/index"
errorIfMaximumExceeded="false"
defaultTargetUrl="/main/index"
alwaysUseDefaultTargetUrl="true"
sniff="true"
xframeOptions="SAMEORIGIN"
xssProtection="true"
cacheControl="false"
csrf="false"
csrfAccessDeniedUrl="/common/csrf"
/>
<egov-security:secured-object-config id="securedObjectConfig"
sqlHierarchicalRoles="
select a.chldrn_role child, a.parnts_role parent
from tb_cms_role_hierarchy a left join tb_cms_role_hierarchy b
on a.chldrn_role = b.parnts_role"
sqlRolesAndUrl="
select a.role_pttrn url, b.author_cd authority
from tb_cms_role_inf a, tb_cms_author_role_ref b
where a.role_cd = b.role_cd
and a.role_ty = 'URL' order by a.role_sort"
sqlRolesAndMethod="
select a.role_pttrn method, b.author_cd authority
from tb_cms_role_inf a, tb_cms_author_role_ref b
where a.role_cd = b.role_cd
and a.role_ty = 'METHOD' order by a.role_sort"
sqlRolesAndPointcut="
select a.role_pttrn pointcut, b.author_cd authority
from tb_cms_role_inf a, tb_cms_author_role_ref b
where a.role_cd = b.role_cd
and a.role_ty = 'POINTCUT' order by a.role_sort"
sqlRegexMatchedRequestMapping="
select a.role_pttrn uri, b.author_cd authority
from tb_cms_role_inf a, tb_cms_author_role_ref b
where a.role_cd = b.role_cd
and a.role_ty = 'URL'"
/>
<egov-security:initializer id="initializer" supportMethod="true" supportPointcut="false" />
</beans>
- sec > security 3가지 파일작업을 해야한다.
- 하나씩 알아보자
- EgovSessionMapping.java
- 아래사진과 같이 LoginVO와 사용자테이블을 매핑한다.
- 아래에 매핑된 변수는 나중에 세션정보로 사용할수 있다.
- password는 실제 패스워드를 값이 아닌 user_id값이다. 프레임워크에서 default로 이렇게 설정해놓았기때문에 그대로사용한다. enabled는 지우면 안된다.
- EgovSpringSecurityLoginFilter.java
- 불필요한 부분은 삭제하였다.
- 주의할점은 session.setAttribute("loginVO", loginVO); 할때 빨간색 loginVO명명규칙은 변경하지 않는다.
- egov.rte 내장함수에 loginVO로 fix되어있다.
- loginVO = loginService.actionLogin(loginVO); 이부분만 본인소스에 맞게 작업하면 된다.
package egovframework.com.sec.security.filter;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.context.support.WebApplicationContextUtils;
import egovframework.com.cmm.helper.EgovUserDetailsHelper;
import egovframework.com.cmm.util.EgovMessageSource;
import egovframework.com.common.login.service.LoginService;
import egovframework.com.common.login.service.LoginVO;
public class EgovSpringSecurityLoginFilter implements Filter {
private FilterConfig config;
private static final Logger LOGGER = LoggerFactory.getLogger(EgovSpringSecurityLoginFilter.class);
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
LOGGER.info("SpringSecurityLoginFilter called...");
String loginURL = config.getInitParameter("loginURL");
loginURL = loginURL.replaceAll("\r", "").replaceAll("\n", "");
String loginProcessURL = config.getInitParameter("loginProcessURL");
loginProcessURL = loginProcessURL.replaceAll("\r", "").replaceAll("\n", "");
ApplicationContext act = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
LoginService loginService = (LoginService) act.getBean("loginService");
EgovMessageSource egovMessageSource = (EgovMessageSource) act.getBean("egovMessageSource");
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession session = httpRequest.getSession();
String requestURL = ((HttpServletRequest) request).getRequestURI();
if (EgovUserDetailsHelper.getAuthenticatedUser() == null || requestURL.contains(loginProcessURL)) {
if (requestURL.contains(loginProcessURL)) {
String id = httpRequest.getParameter("userId");
String password = httpRequest.getParameter("userPw");
if ((id == null || "".equals(id)) && (password == null || "".equals(password))) {
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginURL);
httpRequest.setAttribute("loginMessage", "");
dispatcher.forward(httpRequest, httpResponse);
return;
}else if (password == null || password.equals("") || password.length() < 8 || password.length() > 20) {
httpRequest.setAttribute("loginMessage", egovMessageSource.getMessage("fail.common.login.password",request.getLocale()));
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginURL);
dispatcher.forward(httpRequest, httpResponse);
return;
}
LoginVO loginVO = new LoginVO();
loginVO.setUserId(id);
loginVO.setUserPw(password);
try {
loginVO = loginService.actionLogin(loginVO);
if (loginVO != null && loginVO.getUserId() != null && !loginVO.getUserId().equals("")) {
session.setAttribute("loginVO", loginVO);
UsernamePasswordAuthenticationFilter springSecurity = null;
Map<String, UsernamePasswordAuthenticationFilter> beans = act.getBeansOfType(UsernamePasswordAuthenticationFilter.class);
if (beans.size() > 0) {
springSecurity = (UsernamePasswordAuthenticationFilter) beans.values().toArray()[0];
springSecurity.setUsernameParameter("egov_security_username");
springSecurity.setPasswordParameter("egov_security_password");
springSecurity.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(request.getServletContext().getContextPath() +"/egov_security_login", "POST"));
} else {
throw new IllegalStateException("No AuthenticationProcessingFilter");
}
springSecurity.doFilter(new RequestWrapperForSecurity(httpRequest, loginVO.getUserId(), loginVO.getUserId()), httpResponse, chain);
} else {
httpRequest.setAttribute("loginMessage", egovMessageSource.getMessage("fail.common.login",request.getLocale()));
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginURL);
dispatcher.forward(httpRequest, httpResponse);
return;
}
} catch(IllegalArgumentException e) {
LOGGER.error("[IllegalArgumentException] : "+ e.getMessage());
} catch (Exception ex) {
LOGGER.error("Login Exception : {}", ex.getCause(), ex);
httpRequest.setAttribute("loginMessage", egovMessageSource.getMessage("fail.common.login",request.getLocale()));
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginURL);
dispatcher.forward(httpRequest, httpResponse);
return;
}
return;
}
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
}
class RequestWrapperForSecurity extends HttpServletRequestWrapper {
private String username = null;
private String password = null;
public RequestWrapperForSecurity(HttpServletRequest request, String username, String password) {
super(request);
this.username = username;
this.password = password;
}
@Override
public String getServletPath() {
return ((HttpServletRequest) super.getRequest()).getContextPath() + "/egov_security_login";
}
@Override
public String getRequestURI() {
return ((HttpServletRequest) super.getRequest()).getContextPath() + "/egov_security_login";
}
@Override
public String getParameter(String name) {
if (name.equals("egov_security_username")) {
return username;
}
if (name.equals("egov_security_password")) {
return password;
}
return super.getParameter(name);
}
}
- EgovSpringSecurityLogoutFilter.java
- 이부분은 크게 변경할것이 없다.
package egovframework.com.sec.security.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EgovSpringSecurityLogoutFilter implements Filter{
@SuppressWarnings("unused")
private FilterConfig config;
private static final Logger LOGGER = LoggerFactory.getLogger(EgovSpringSecurityLogoutFilter.class);
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String requestURL = ((HttpServletRequest)request).getRequestURI();
LOGGER.debug(requestURL);
((HttpServletRequest)request).getSession().setAttribute("loginVO", null);
((HttpServletResponse)response).sendRedirect(((HttpServletRequest)request).getContextPath() + "/egov_security_logout");
}
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
}
}
- EgovWebApplicationInitializer.java
- 마지막으로 제일중요한 작업이다. 서블릿 2.0대 버전 에서는 시큐리티 필터를 web.xml에서 작성을 하였으나 3점대 버전에서는 java파일인 해당파일에서 설정을 한다.
- 시큐리티 영역에서 불필요한 부분은 삭제하고 아래코드만 유지한다.
- context-security.xml 파일에서 작성한 loginURL / loginProcessURL / 로그아웃 URL 등을 아래코드에 넣는다.
/** 스프링 시큐리티 START **/
FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());
springSecurityFilterChain.addMappingForUrlPatterns(null, false, "*");
servletContext.addListener(new org.springframework.security.web.session.HttpSessionEventPublisher());
FilterRegistration.Dynamic egovSpringSecurityLoginFilter = servletContext.addFilter("egovSpringSecurityLoginFilter", new EgovSpringSecurityLoginFilter());
egovSpringSecurityLoginFilter.setInitParameter("loginURL", "/login/index");
egovSpringSecurityLoginFilter.setInitParameter("loginProcessURL", "/login/login_process");
egovSpringSecurityLoginFilter.addMappingForUrlPatterns(null, false, "/login/*");
FilterRegistration.Dynamic egovSpringSecurityLogoutFilter = servletContext.addFilter("egovSpringSecurityLogoutFilter", new EgovSpringSecurityLogoutFilter());
egovSpringSecurityLogoutFilter.addMappingForUrlPatterns(null, false, "/login/logout");
/** 스프링 시큐리티 END **/
3. 테스트
- 테스트관련 소스이다. 분량이 많아서 부분적으로만 작성한다.
# login.jsp
<input type="text" name="userId" id="userId" placeholder="아이디" value=""/>
<input type="password" name="userPw" id="userPw" placeholder="비밀번호" value="">
document.loginForm.action="/login/login_process";
document.loginForm.submit();
# main.jsp
${userId}님 환영합니다.
로그아웃시 스크립트 : document.location.href="/login/logout";
# MainController.java
# /main/index URL 호출시
try {
boolean isAuthenticated = EgovUserDetailsHelper.isAuthenticated();
LoginVO user = (LoginVO) EgovUserDetailsHelper.getAuthenticatedUser();
if(!isAuthenticated) {
ModelAndView modelAndView = new ModelAndView("redirect:/login/index");
throw new ModelAndViewDefiningException(modelAndView);
}
model.addAttribute("userId", user.getUserId());
} catch (Exception e) {
e.getMessage();
}
return "main/index";
# 로그인SQL
SELECT a.user_id
, a.user_pw
, a.user_nm
, a.email_adres
, a.mbtl_no
FROM cms.tb_user a
WHERE a.user_id = #{userId}
AND a.user_pw = #{userPw}
4. 마치며
- egovframework에서 제공하는 스프링 시큐리티 자체가 포스팅하기 사실 어렵다. 확인해야할부분, 데이터베이스 부분도 필수적으로 사용해야되고, 그러하다.
- 위내용대로 따라해도 안되는 경우가 발생할수 있다. 개개인의 개발환경이 다 다르다. 최대한 도움이 되었으면 좋겠다.
반응형
'개발 > egovframework' 카테고리의 다른 글
[전자정부프레임워크] DB접속정보 암호화 - crypto 서비스 (1) | 2023.05.03 |
---|---|
[전자정부프레임워크] Id Generation 시퀀스 관리 (0) | 2023.05.02 |
[전자정부프레임워크] 스프링시큐리티 로그인 적용(1/2) (0) | 2023.03.30 |
[전자정부프레임워크] Spring quartz 스케줄러 사용법 (0) | 2023.03.30 |
[전자정부프레임워크] Spring profile (운영,개발 분리) (0) | 2023.03.22 |