-> 차근차근 개발하는 과정을 올릴려고 했는데 중간 중간에 과정을 올리기가 힘들어서 전체 소스를 한번에 정리해서 이번장에 올리겠습니다.
게시판 생성에 앞서 테이블 생성 |
CREATE TABLE `tb_board` (
`BOARD_IDX` int NOT NULL AUTO_INCREMENT COMMENT '인덱스',
`BOARD_TITLE` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '타이틀',
`BOARD_CONTENT` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '내용',
`REG_ID` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '등록자',
`VIEW_COUNT` int DEFAULT '0' COMMENT '조회수',
`USE_YN` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'Y' COMMENT '사용여부',
`REG_DATE` datetime DEFAULT NULL COMMENT '등록일자',
PRIMARY KEY (`BOARD_IDX`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COMMENT='게시판'
admin 프로젝트 java 파일들 |
AdminApllication.java |
package com.board.admin;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import com.board.common.Constants;
@SpringBootApplication
@ComponentScan(basePackages = Constants.APP_DEFAULT_PACKAGE_NAME)
@MapperScan(basePackages = Constants.MAPPER_PACKAGE)
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
-> 해당 파일은 admin run파일입니다.
ServletInitializer.java |
package com.board.admin;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AdminApplication.class);
}
}
-> 해당파일은 war파일로 배포를 진행할경우 사용합니다. Spring Boot의 전통방식인 jar를 사용할경우에는 사용하지 않습니다.
Constants.java |
package com.board.common;
/** 서비스에 사용되는 공통변수 */
public final class Constants {
//다국어 Cookie 이름
public static final String APP_LOCALE_COOKIE = "APP_LOCALE";
//프로젝트 패키지 이름
public final static String APP_DEFAULT_PACKAGE_NAME = "com.board";
//dao 패키지 경로
public final static String MAPPER_PACKAGE = Constants.APP_DEFAULT_PACKAGE_NAME+".dao";
//Tiles xml 경로
public final static String[] TILES_LAYOUT_XML_PATH = {
"WEB-INF/tiles.xml"
};
//Runtime에서 JSP의 refresh 적용 여부
public final static boolean REFRESH_JSP_ON_RUNTIME = true;
/** 정적 리소스 종류 */
private final static String[] STATIC_RES = {
"/js"
,"/css"
,"/images"
,"/favicon"
,"/template"
,"/font"
,"/robots.txt"
,"/favicon.ico"
};
/** 정적 리소스 매핑 URL 패턴 (위에꺼랑 순서 맞아야함) */
public final static String[] STATIC_RESOURCES_URL_PATTERNS = {
STATIC_RES[0]+"/**"
,STATIC_RES[1]+"/**"
,STATIC_RES[2]+"/**"
,STATIC_RES[3]+"/**"
,STATIC_RES[4]+"/**"
,STATIC_RES[5]+"/**"
,STATIC_RES[6]
,STATIC_RES[7]+"/**"
};
/** 정적 리소스 기본 페이지 classpath */
private static final String STATIC_PATH = "classpath:/static";
/** 정적 리스스 위치 */
public final static String[] CLASSPATH_RESOURCE_LOCATIONS = {
STATIC_PATH+STATIC_RES[0]+"/"
,STATIC_PATH+STATIC_RES[1]+"/"
,STATIC_PATH+STATIC_RES[2]+"/"
,STATIC_PATH+STATIC_RES[3]+"/"
,STATIC_PATH+STATIC_RES[4]+"/"
,STATIC_PATH+STATIC_RES[5]+"/"
,STATIC_PATH+STATIC_RES[6]
,STATIC_PATH+STATIC_RES[7]+"/"
};
public static final int DEFAULT_PAGE_NUMBER = 1;
public static final int DEFAULT_PAGE_SIZE = 10;
// server health check url
public static final String HEALTH_CHECK_URL = "/healthCheck";
}
-> 해당 파일은 서비스에서 사용할 공통 변수를 설정해놓은 파일입니다.
CoTopComponent.java |
package com.board.common;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import com.google.gson.JsonObject;
/* 최상위 컴포넌트 */
public abstract class CoTopComponent {
protected void writeResponse(HttpServletResponse res, JsonObject jsonObject) throws IOException{
writeResponse(res, jsonObject.toString());
}
protected void writeResponse(HttpServletResponse res, String message) throws IOException{
res.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE);
PrintWriter pw = res.getWriter();
pw.write(message);
pw.close();
}
protected String makePageDispatcherUrl(String requestUrl, String dispatcherPath) {
return Url.TILES_AJAX + dispatcherPath + requestUrl.substring(requestUrl.lastIndexOf("/"));
}
}
-> 해당 파일은 공통 메소드를 설정한 파일입니다.
Paginator.java |
package com.board.common;
import java.io.Writer;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import lombok.Setter;
/**
*
*/
@Setter
public class Paginator extends SimpleTagSupport {
// ******************************************************************
// * 페이지 네비게이터 템플릿 (Debult) *
// ******************************************************************
private String MainSB ="";
private String FistB ="<li><a class='#$class' href='javascript:;' onclick='#$goPage(##);'>#$title</a></li>";
private String PrevB ="<li><a class='#$class' href='javascript:;' onclick='#$goPage(##);'>#$title</a></li>";
private String CurB ="<li><a class='#$class' href='javascript:;'>#$title</a></li>";
private String OthB ="<li><a class='#$class' href='javascript:;' onclick='#$goPage(##);'>#$title</a></li>";
private String NextB ="<li><a class='#$class' href='javascript:;' onclick='#$goPage(##);'>#$title</a><li>";
private String LastB ="<li><a class='#$class' href='javascript:;' onclick='#$goPage(##);'>#$title</a></li>";
private String MainEB ="";
String goPageScript = "goPage"; // 이동 이벤트 Script Function Name (해당 스크립트의 인자는 페이지 번호만 받아야함, 추가 검색조건은 함수내에서 처리할 것) (설정은 JSTL 속성으로 입력)
int curPage; // 현재 페이지 (설정은 JSTL 속성으로 입력)
int totPages; // 전체 페이지 (설정은 JSTL 속성으로 입력)
int blockSize = 10; // 블록 크기 (설정은 JSTL 속성으로 입력)
int skipSize = 1; // 이전/다음 선택시 이동할 페이지 갯수 (설정은 JSTL 속성으로 입력)
// Options
boolean goFirstUse = false; // 처음 페이지 사용 여부 (설정은 JSTL 속성으로 입력)
boolean goLastUse = false; // 마지막 페이지 사용 여부 (설정은 JSTL 속성으로 입력)
// CSS Class
private String paginatorBlockClass = ""; // paginator 전체 메인 블록 (설정은 JSTL 속성으로 입력)
private String goFirstClass = ""; // 처음 페이지 (설정은 JSTL 속성으로 입력)
private String prevPageClass = "prev"; // 이전 페이지 (설정은 JSTL 속성으로 입력)
private String curPageClass = "on"; // 현재 페이지 (설정은 JSTL 속성으로 입력)
private String defaultPageClass = ""; // 다른 페이지 (설정은 JSTL 속성으로 입력)
private String nextPageClass = "next"; // 다음 페이지 (설정은 JSTL 속성으로 입력)
private String goLastClass = ""; // 마지막 페이지 (설정은 JSTL 속성으로 입력)
// Label
private String goFirstLabel = "처음"; // 처음 페이지 타이틀 (설정은 JSTL 속성으로 입력)
private String prevPageLabel = "이전"; // 이전 페이지 타이틀 (설정은 JSTL 속성으로 입력)
private String nextPageLabel = "다음"; // 다음 페이지 타이틀 (설정은 JSTL 속성으로 입력)
private String goLastLabel = "마지막"; // 마지막 페이지 타이틀 (설정은 JSTL 속성으로 입력)
private Writer getWriter() {
JspWriter out = getJspContext().getOut();
return out;
}
@Override
public void doTag() throws JspException {
Writer out = getWriter();
// Spring pageable의 page 번호가 0부터 시작하기 때문에 +1하고
// 실제로 페이지 링크를 생성하는 부분에서 다시 -1 한다.
curPage ++;
boolean lastPage = curPage == totPages;
int pgStart = Math.max(curPage - blockSize / 2, 1);
int pgEnd = pgStart + blockSize;
if (pgEnd > totPages + 1) {
int diff = pgEnd - totPages;
pgStart -= diff - 1;
if (pgStart < 1)
pgStart = 1;
pgEnd = totPages + 1;
}
if(totPages > 0) {
try {
out.write(MainSB.replace("#$class", paginatorBlockClass));
// 처음
if (goFirstUse) out.write( replacePageStr(FistB, goFirstClass, goPageScript, 1, goFirstLabel) );
// 이전
if (curPage > 1){
int moveSize = curPage-skipSize;
if (moveSize < 1) moveSize = 1;
out.write( replacePageStr(PrevB, prevPageClass, goPageScript, moveSize, prevPageLabel) );
}
for (int i = pgStart; i < pgEnd; i++) {
// 현재 페이지
if (i == curPage) out.write( replacePageStr(CurB, curPageClass, goPageScript, curPage, String.valueOf(curPage)) );
// 기타 페이지
else out.write( replacePageStr(OthB, defaultPageClass, goPageScript, i, String.valueOf(i)) );
}
// 다음
if (!lastPage){
int moveSize = curPage + skipSize;
if (moveSize > totPages) moveSize = totPages;
out.write( replacePageStr(NextB, nextPageClass, goPageScript, moveSize, nextPageLabel) );
}
// 마지막
if (goLastUse) out.write( replacePageStr(LastB, goLastClass, goPageScript, totPages, goLastLabel) );
out.write(MainEB);
} catch (java.io.IOException ex) {
throw new JspException("Error in Paginator tag", ex);
}
}
}
private String replacePageStr(String srcStr, String classStr, String scriptStr, int pageNum, String labelStr) {
pageNum --;
return srcStr.replace("#$class", classStr).replace("#$goPage", scriptStr).replace("##", String.valueOf(pageNum)).replace("#$title", labelStr);
}
}
-> 해당파일은 페이징 처리 관련된 java 파일입니다. 이파일에서 버튼의 html 형식을 수정할수 있습니다.
Url.java |
package com.board.common;
/* api url 정의 */
public final class Url {
public static final String TILES_ROOT = "/tiles/view";
public static final String TILES_SINGLE = "/tiles/single";
public static final String TILES_AJAX = "/tiles/ajax";
/* 로그인 */
public static final class AUTH {
/* 로그인 url */
public static final String LOGIN = "/auth/login";
/* 로그인 jsp */
public static final String LOGIN_JSP = TILES_SINGLE + "/auth/login";
/* 회원가입 url */
public static final String JOIN = "/auth/join";
/* 회원가입 jsp */
public static final String JOIN_JSP = TILES_ROOT + "/auth/join";
/* 사용자 등록 */
public static final String INSERT_USER = "/auth/insertUser";
/* 로그인 인증 요청 */
public static final String LOGIN_PROC = "/auth/login-proc";
/* 로그아웃 요청 */
public static final String LOGOUT_PROC = "/auth/logout-proc";
}
/* 메인 화면 */
public static final class MAIN {
public static final String _MAIN_AJAX_ROOT_PATH = "/main/ajax";
/* 메인 url */
public static final String MAIN = "/";
/* 메인 jsp */
public static final String MAIN_JSP = TILES_ROOT + "/main/list";
/* 메인 리스트 ajax */
public static final String MAIN_LIST_AJAX = _MAIN_AJAX_ROOT_PATH + "/list-view";
/* 메인 글쓰기 */
public static final String MAIN_WRITE = "/board/write";
/* 메인 글쓰기 jsp */
public static final String MAIN_WRITE_JSP = TILES_ROOT + "/main/write";
/* 메인 수정화면 */
public static final String MAIN_UPDATE = "/board/update";
/* 메인 글쓰기 jsp */
public static final String MAIN_UPDATE_JSP = TILES_ROOT + "/main/update";
}
}
-> 해당 파일은 Controller에 사용될 url을 모아놓은 class 파일입니다.
AdminAuthenticationProvider.java |
package com.board.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import com.board.dao.UserMapper;
import com.board.entity.UserAuthority;
import com.board.entity.UserInfo;
import com.board.exception.UserAuthException;
import com.board.exception.UserIdException;
import org.springframework.security.crypto.password.PasswordEncoder;
/** 로그인 provider */
@Component
public class AdminAuthenticationProvider implements AuthenticationProvider{
@Autowired
UserMapper userMapper;
@Autowired
PasswordEncoder passwordEncoder;
@SuppressWarnings("unchecked")
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken authToken = (UsernamePasswordAuthenticationToken) authentication;
//아이디
String id = (authToken.getName()).toUpperCase();
//비밀번호
String pwd = (String) authToken.getCredentials();
//해당 사용자 정보 조회
UserInfo userInfo = userMapper.getUserInfo(id);
//해당 사용자가 있을경우
if(userInfo != null ) {
//입력한 비밀번호가 현재 비밀번호와 같지 않으면
if(!(passwordEncoder.matches(pwd, userInfo.getPassword()))) {
throw new UserIdException("접속 할 수 없습니다. \n아이디 또는 비밀번호를 확인해주세요.");
}
//입력한 비밀번호가 현재 비밀번호와 같으면
else {
List<UserAuthority> authorities = new ArrayList<>();
//권한 조회
UserAuthority getUserAuthorities = userMapper.getUserAuthorities(authToken.getName());
//권한이 있을경우
if(getUserAuthorities != null) {
UserAuthority auth = new UserAuthority();
auth.setAuthority(getUserAuthorities.getAuthority());
auth.setAuthorityNm(getUserAuthorities.getAuthorityNm());
authorities.add(auth);
userInfo.setAuthority(getUserAuthorities.getAuthority());
userInfo.setAuthorityNm(getUserAuthorities.getAuthorityNm());
}
//권한이 없을 경우
else {
throw new UserAuthException("접속권한이 없습니다. \n관리자에게 권한을 요청해주세요.");
}
}
}
//해당 사용자가 없을경우
else {
throw new UserIdException("접속 할 수 없습니다. \n아이디 또는 비밀번호를 확인해주세요.");
}
List<GrantedAuthority> authorities = (List<GrantedAuthority>) userInfo.getAuthorities();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userInfo, "1", authorities);
return token;
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
-> 해당 파일은 로그인을 시도하였을때 provider에서 로그인이 정상적인지 체크하는 부분입니다.
AjaxInterceptor.java |
package com.board.config;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AjaxInterceptor implements HandlerInterceptor {
/* (non-Javadoc)
* @see org.springframework.web.servlet.HandlerInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
boolean result = true;
//tmptmp
// if(false) {
try {
SecurityContext sc = SecurityContextHolder.getContext();
Authentication auth = sc.getAuthentication();
if(auth == null || auth.getPrincipal() == null || "anonymousUser".equals(auth.getPrincipal())) {
//Ajax 콜인지 아닌지 판단
if(isAjaxRequest(request)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
log.debug(e.getMessage());
return false;
}
// }
return result;
}
/**
* Checks if is ajax request.
*
* @param req the req
* @return true, if is ajax request
*/
private boolean isAjaxRequest(HttpServletRequest req) {
/*
// 사용자 인증이 필요 없는 URL 체크
String requestUri = RequestUtil.getRequestURI(req);
if(requestUri.startsWith("/auth/")) {
return false;
}
*/
String ajaxHeader = "AJAX";
return req.getHeader(ajaxHeader) != null && req.getHeader(ajaxHeader).equals(Boolean.TRUE.toString());
}
/* (non-Javadoc)
* @see org.springframework.web.servlet.HandlerInterceptor#postHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, org.springframework.web.servlet.ModelAndView)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
/* (non-Javadoc)
* @see org.springframework.web.servlet.HandlerInterceptor#afterCompletion(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
-> 로그인을 하지 않았을경우에는 통신을 할수없게 제어하는 인터셉터입니다.
LoginFailureHandler.java |
package com.board.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import com.board.common.CoTopComponent;
import com.board.exception.UserAuthException;
import com.board.exception.UserIdException;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
/* 로그인 실패시 타는 핸들러 */
@Slf4j
@Component
public class LoginFailureHandler extends CoTopComponent implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
writeResponse(response, parseException(request.getParameter("un"), exception));
}
private JsonObject parseException(String userName, AuthenticationException exception) {
String errCode = "99";
String errMsg = exception.getMessage();
//존재하지 않는 아이디
if( exception instanceof UserIdException) {
log.error("존재하지않는 아이디입니다.");
}
//권한이 없을경우
else if( exception instanceof UserAuthException) {
log.error("권한이 없는 아이디입니다.");
}
JsonObject result = new JsonObject();
result.addProperty("resultCode", errCode);
result.addProperty("resultMessage", errMsg);
return result;
}
}
-> 로그인 실패시 타는 핸들러
LoginSuccessHandler.java |
package com.board.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.board.common.CoTopComponent;
import com.board.entity.UserInfo;
import com.google.gson.JsonObject;
/* 로그인 성공시 타는 handler */
@Component
public class LoginSuccessHandler extends CoTopComponent implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication auth) throws IOException, ServletException {
//default 성공
String resultCode = "00";
HttpSession session = request.getSession(true);
session.setMaxInactiveInterval(60 * 60 * 3);
session.setAttribute("sessUserInfo",((UserInfo) auth.getPrincipal()));
//Response 결과 값을 넣어줌
JsonObject loginResult = new JsonObject();
loginResult.addProperty("resultCode", resultCode);
loginResult.addProperty("targetUrl", request.getContextPath()+"/");
//응답 전송
writeResponse(response, loginResult);
}
}
-> 로그인 성공시 타는 핸들러
SecurityConfig.java |
package com.board.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.board.common.Constants;
import com.board.common.Url;
@Configuration
@EnableWebSecurity
@ComponentScan(value = Constants.APP_DEFAULT_PACKAGE_NAME)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired private LoginSuccessHandler loginSuccessHandler;
@Autowired private LoginFailureHandler loginFailureHandler;
// @Autowired private CustomerUserDetailsService customUserDetailsService;
/**
* Configure.
*
* @param auth the auth
* @throws Exception the exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(customUserDetailsService);
auth.authenticationProvider(new AdminAuthenticationProvider());
super.configure(auth);
}
/**
* http 요청 검사.
*
* @param http the http
* @throws Exception the exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// replay 어택을 막기 위한 csrf 토큰의 생성을 비활성화(disabled) 처리
.csrf().disable()
// pdf viewer 에서 'X-Frame-Options' to 'DENY' 대응
.headers().frameOptions().disable().and()
// 요청에 대한 권한 매핑
.authorizeRequests()
.antMatchers( "/auth/**" ).permitAll() // 패스워드찾기,회원가입
.antMatchers( "/" ).permitAll()
.antMatchers( "/**/ajax/**" ).permitAll()
.antMatchers( "/board/**" ).permitAll()
.antMatchers( "/resource/**/images/**" ).permitAll() // image
.anyRequest().authenticated() // 모든 요청에 대해 권한 확인이 필요
// .anyRequest().permitAll()
.and()
// 로그인 화면 설정
.formLogin()
.permitAll()
.loginPage( Url.AUTH.LOGIN )
.loginProcessingUrl( Url.AUTH.LOGIN_PROC )
.successHandler( loginSuccessHandler )
.failureHandler( loginFailureHandler )
.usernameParameter( USERNAME_PARAM )
.passwordParameter( PASSWORD_PARAM )
.and()
.logout()
.logoutUrl( Url.AUTH.LOGOUT_PROC )
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.and()
// 세션 관리
.sessionManagement()
.maximumSessions(200) /* session 허용 갯수 */
.expiredUrl(Url.AUTH.LOGIN) /* session 만료시 이동 페이지*/
.sessionRegistry(sessionRegistry()) // 세션을 목록에 담아둠
.maxSessionsPreventsLogin(true) /* 동일한 사용자 로그인시 x, false 일 경우 기존 사용자 */
;
}
/**
* web요청 검사.
*
* @param web the web
* @throws Exception the exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
// Security Debug
// web.debug(true);
web
.ignoring()
// static 리소스 경로는 webSecurity 검사 제외
.antMatchers( Constants.STATIC_RESOURCES_URL_PATTERNS )
.antMatchers( Constants.HEALTH_CHECK_URL )
.antMatchers(HttpMethod.GET, "/exception/**")
;
super.configure(web);
}
/**
* PasswordEncoder.
*
* @return the password encoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SessionRegistry sessionRegistry() {
return new SpringSecuritySessionRegistImpl();
}
/**
* AuthenticationProvider
* <br>관리자의 계정정보를 통해 로그인 인증을 처리합니다.
*
* @return the authentication provider
* @see kr.mediaflow.fdwm.config.DatabaseConfig
*/
// @Bean
// public AuthenticationProvider daoAuthenticationProvider() {
// DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
// impl.setUserDetailsService(customUserDetailsService);
// impl.setPasswordEncoder(new BCryptPasswordEncoder());
// impl.setHideUserNotFoundExceptions(false);
// return impl;
// }
/** 관리자 아이디 파라미터 이름 : {@value #USERNAME_PARAM}. */
public static final String USERNAME_PARAM = "un";
/** 관리자 비밀번호 파라미터 이름 : {@value #PASSWORD_PARAK}. */
public static final String PASSWORD_PARAM = "up";
}
-> web에서 해당 url은 특정한 보안 절차를 거친 사용자들만 접속할수 있게 설정하는 config파일입니다.
SpringSecuritySessionRegistImpl.java |
package com.board.config;
import org.springframework.security.core.session.SessionRegistryImpl;
public class SpringSecuritySessionRegistImpl extends SessionRegistryImpl {
}
TilesConfig.java |
package com.board.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.springframework.web.servlet.view.tiles3.TilesConfigurer;
import org.springframework.web.servlet.view.tiles3.TilesView;
import com.board.common.Constants;
@Configuration
public class TilesConfig {
@Bean
public TilesConfigurer tilesConfigurer() {
final TilesConfigurer configurer = new TilesConfigurer();
configurer.setDefinitions(Constants.TILES_LAYOUT_XML_PATH);
configurer.setCheckRefresh(true);
return configurer;
}
/**
* UrlBased 뷰 리졸버.
*
* @return urlViewResolver
*/
@Bean
public UrlBasedViewResolver urlViewResolver() {
UrlBasedViewResolver tilesViewResolver = new UrlBasedViewResolver();
tilesViewResolver.setViewClass(TilesView.class);
return tilesViewResolver;
}
}
-> tiles 설정파일입니다.
WebApplicationContextLocator.java |
package com.board.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
@Configuration
public class WebApplicationContextLocator implements ServletContextInitializer {
private static WebApplicationContext webApplicationContext;
public static WebApplicationContext getCurrentWebApplicationContext() {
return webApplicationContext;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
}
}
WebConfig.java |
package com.board.config;
import java.util.List;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.resource.PathResourceResolver;
import com.board.common.Constants;
import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter;
/**
* The Class WebConfig.
*/
@Configuration
@EnableWebMvc
@EnableCaching
@ComponentScan
public class WebConfig implements WebMvcConfigurer {
/**
* 인터셉터 관리.
*
* @param registry the registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new LogInterceptor()); // 로그 인터셉터
registry.addInterceptor(localeChangeInterceptor()); // 로케일 변경 인터셉터
registry.addInterceptor(new AjaxInterceptor()).excludePathPatterns("/error", "/error/**","/viewer","/viewer/**", "/css/**", "/js/**", "/images/**");
}
/**
* Resource 핸들러.
*
* @param registry the registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler( Constants.STATIC_RESOURCES_URL_PATTERNS)
.addResourceLocations(Constants.CLASSPATH_RESOURCE_LOCATIONS)
.setCachePeriod(60*60*24*7)// 60*60*24*7 => 일주일
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
/**
* Adds the argument resolvers.
*
* @param argumentResolvers the argument resolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setFallbackPageable(PageRequest.of(Constants.DEFAULT_PAGE_NUMBER, Constants.DEFAULT_PAGE_SIZE));
argumentResolvers.add(resolver);
WebMvcConfigurer.super.addArgumentResolvers(argumentResolvers);
}
/**
* Active profile.
*
* @param profile the profile
* @return the string
*/
@Bean
@Value("${spring.profiles.active}")
public String activeProfile(String profile) {
return profile;
}
/**
* Locale Resolver.
*
* @return the locale resolver
*/
@Bean
public LocaleResolver localeResolver() {
// SessionLocaleResolver slr = new SessionLocaleResolver();
// slr.setDefaultLocale(Locale.KOREAN);
CookieLocaleResolver slr = new CookieLocaleResolver();
slr.setDefaultLocale(Locale.KOREAN);
slr.setCookieName(Constants.APP_LOCALE_COOKIE);
return slr;
}
/**
* Locale Change Interceptor.
*
* @return the locale change interceptor
*/
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Bean
public FilterRegistrationBean<XssEscapeServletFilter> filterRegistrationBean() {
FilterRegistrationBean<XssEscapeServletFilter> filterRegistration = new FilterRegistrationBean<>();
filterRegistration.setFilter(new XssEscapeServletFilter());
filterRegistration.setOrder(1);
filterRegistration.addUrlPatterns("/*");
return filterRegistration;
}
// /**
// * Message source.
// *
// * @return the message source
// */
// @Bean
// public MessageSource messageSource() {
// ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// // WEB-INF 밑에 해당 폴더에서 properties를 찾는다.
// messageSource.setBasename("message/messages");
// messageSource.setDefaultEncoding("UTF-8");
// return messageSource;
// }
}
LoginController.java |
package com.board.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.board.common.Url;
import com.board.entity.UserInfo;
import com.board.service.LoginService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class LoginController {
@Autowired
LoginService loginService;
//로그인 화면
@GetMapping(value= {Url.AUTH.LOGIN})
public String login() {
return Url.AUTH.LOGIN_JSP;
}
//회원가입 화면
@GetMapping(Url.AUTH.JOIN)
public String join() {
return Url.AUTH.JOIN_JSP;
}
//사용자 등록
@PostMapping(Url.AUTH.INSERT_USER)
@ResponseBody
public Map<String, Object> insertUser(@ModelAttribute UserInfo userInfo) {
//회원 등록
return loginService.checkLoginInsert(userInfo);
}
}
-> 로그인 controller입니다.
MainController.java |
package com.board.controller;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.board.common.CoTopComponent;
import com.board.common.Constants;
import com.board.common.Url;
import com.board.common.Url.MAIN;
import com.board.dao.MainMapper;
import com.board.service.MainService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class MainController extends CoTopComponent {
@Autowired MainService mainService;
@Autowired MainMapper mainMapper;
//profile
@Value("${spring.profile.active")
private String profile;
//메인화면
@GetMapping(Url.MAIN.MAIN)
public String main() {
return Url.MAIN.MAIN_JSP;
}
//메인화면 리스트 ajax
@GetMapping(Url.MAIN.MAIN_LIST_AJAX)
public String mainListAjax(@RequestParam Map<String, Object> params
,Pageable pageable
,Model model
) {
model.addAttribute("boardList", mainService.selectBoardList(params, pageable, Integer.parseInt(params.get("size").toString())));
model.addAttribute("resultDataTotal", mainMapper.selectBoardListCnt(params));
return makePageDispatcherUrl(MAIN.MAIN_LIST_AJAX, MAIN._MAIN_AJAX_ROOT_PATH);
}
//글쓰기화면
@GetMapping(Url.MAIN.MAIN_WRITE)
public String write() {
return Url.MAIN.MAIN_WRITE_JSP;
}
//글등록
@ResponseBody
@PostMapping(Url.MAIN.MAIN_WRITE)
public Map<String, Object> writeSubmit(@RequestBody Map<String, Object> params) {
log.info("params={}", params);
mainMapper.insertBoard(params);
return params;
}
//수정화면
@GetMapping(Url.MAIN.MAIN_UPDATE+"/{boardIdx}")
public String update(@PathVariable("boardIdx") String boardIdx, Model model) {
log.info("boardIdx={}", boardIdx);
model.addAttribute("boardInfo", mainMapper.selectBoard(boardIdx));
model.addAttribute("boardIdx", boardIdx);
int viewCount = (int) mainMapper.selectBoard(boardIdx).get("viewCount");
log.info("viewCount={}", viewCount);
Map<String, Object> params = new HashMap<String, Object>();
viewCount++;
params.put("boardIdx", boardIdx);
params.put("viewCount", viewCount);
//조회수 업데이트
mainMapper.updateViewCount(params);
return Url.MAIN.MAIN_UPDATE_JSP;
}
//글수정
@ResponseBody
@PostMapping(Url.MAIN.MAIN_UPDATE)
public Map<String, Object> updateSubmit(@RequestBody Map<String, Object> params) {
log.info("params={}", params);
mainMapper.updateBoard(params);
return params;
}
//server health check
@RequestMapping(value= { Constants.HEALTH_CHECK_URL }, produces=MediaType.TEXT_HTML_VALUE)
public void healthCheck( HttpServletRequest req, HttpServletResponse res ) throws IOException {
String ip = req.getHeader("X-FORWARDED-FOR");
if (ip == null) ip = req.getRemoteAddr();
PrintWriter pw = res.getWriter();
pw.write(" - Active Profile : " + profile + "\n");
pw.write(" - Client IP : " + ip);
pw.close();
}
}
-> 메인 Controller입니다.
UserAuthException.java |
package com.board.exception;
import org.springframework.security.core.AuthenticationException;
/**
* The Class CUserNotFoundException.
*/
public class UserAuthException extends AuthenticationException {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Instantiates a new c user not found exception.
*
* @param msg the msg
* @param t the t
*/
public UserAuthException(String msg, Throwable t) {
super(msg, t);
}
/**
* Instantiates a new c user not found exception.
*
* @param msg the msg
*/
public UserAuthException(String msg) {
super(msg);
}
}
-> 사용자 로그인 권한 관련된 exception
UserIdException.java |
package com.board.exception;
import org.springframework.security.core.AuthenticationException;
/**
* The Class CUserNotFoundException.
*/
public class UserIdException extends AuthenticationException {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/**
* Instantiates a new c user not found exception.
*
* @param msg the msg
* @param t the t
*/
public UserIdException(String msg, Throwable t) {
super(msg, t);
}
/**
* Instantiates a new c user not found exception.
*
* @param msg the msg
*/
public UserIdException(String msg) {
super(msg);
}
}
-> 사용자 로그인 아이디 접속 관련 exception
java 부분은 정리가 끝났습니다. 아래는 jsp와 tld, xml 정리한 부분입니다. |
header.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar" aria-expanded="true" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">게시판 예제</a>
</div>
<div id="navbar" class="navbar-collapse collapse in" aria-expanded="true" style="">
<ul class="nav navbar-nav">
<li class="active"><a href="#">게시판</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<c:choose>
<c:when test="${empty sessUserInfo.authority}">
<li><a href="#">회원가입</a></li>
<li><a href="/auth/login">로그인</a></li>
</c:when>
<c:otherwise>
<li><a href="/auth/logout-proc">로그아웃</a></li>
</c:otherwise>
</c:choose>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
meta.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시판예제</title>
scripts.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!-- 합쳐지고 최소화된 최신 자바스크립트 -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://code.jquery.com/jquery-migrate-1.4.1.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/util.js?latest=${version}"></script>
styles.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!-- 합쳐지고 최소화된 최신 CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<!-- 부가적인 테마 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
error-template.jsp |
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<tiles:insertAttribute name="contents"/>
</body>
</html>
constants.jsp |
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="paginator" uri="/WEB-INF/tlds/paginator.tld" %>
<c:set var="version" value="<%=new java.util.Date()%>" />
<c:set var="ctxPath" value="${pageContext.request.contextPath eq '/' ? '' : pageContext.request.contextPath}" />
<c:set var="remoteURI" value="${ctxPath}${requestScope['javax.servlet.forward.servlet_path']}" />
<c:set var="orgRemoteURI" value="${requestScope['javax.servlet.forward.servlet_path']}" />
<sec:authentication property="principal" var="principal" />
template.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<tiles:insertAttribute name="header"/>
<tiles:insertAttribute name="meta"/>
<tiles:insertAttribute name="styles"/>
</head>
<body>
<tiles:insertAttribute name="scripts"/>
<tiles:insertAttribute name="contents"/>
<tiles:insertAttribute name="contents-js"/>
</body>
</html>
template-simple.jsp |
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<tiles:insertAttribute name="contents"/>
paginator.tld |
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>paginator</short-name>
<uri>/WEB-INF/tlds/paginator</uri>
<tag>
<name>print</name>
<tag-class>com.board.common.Paginator</tag-class>
<body-content>empty</body-content>
<attribute>
<name>goPageScript</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>curPage</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>int</type>
</attribute>
<attribute>
<name>totPages</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>int</type>
</attribute>
<attribute>
<name>blockSize</name>
<rtexprvalue>true</rtexprvalue>
<type>int</type>
</attribute>
<attribute>
<name>skipSize</name>
<rtexprvalue>true</rtexprvalue>
<type>int</type>
</attribute>
<attribute>
<name>paginatorBlockClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>prevPageClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>prevPageLabel</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>nextPageClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>nextPageLabel</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>curPageClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>defaultPageClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>goFirstUse</name>
<rtexprvalue>true</rtexprvalue>
<type>bool</type>
</attribute>
<attribute>
<name>goFirstLabel</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>goFirstClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>goLastUse</name>
<rtexprvalue>true</rtexprvalue>
<type>bool</type>
</attribute>
<attribute>
<name>goLastLabel</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>goLastClass</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
join.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/loginForm.css"/>
<div class="wrapper">
<div id="formContent">
<!-- Tabs Titles -->
<!-- Icon -->
<div class="fadeIn first">
회원가입
</div>
<!-- Login Form -->
<form>
<input type="text" id="userId" class="fadeIn second" name="login" placeholder="아이디를 입력해주세요." required>
<input type="text" id="password" class="fadeIn third" name="login" placeholder="비밀번호를 입력해주세요." required>
<input type="text" id="userName" class="fadeIn third" name="login" placeholder="이름을 입력해주세요.">
<input type="text" id="email" class="fadeIn third" name="login" placeholder="이메일을 입력해주세요.">
<input type="text" id="handPhoneNo" class="fadeIn third" name="login" placeholder="핸드폰번호를 입력해주세요.">
<input type="button" class="fadeIn fourth" value="Join" onclick="join()">
</form>
<!-- Remind Passowrd -->
<div id="formFooter">
<a class="underlineHover" href="/auth/login">뒤로가기</a>
</div>
</div>
</div>
join.js.jsp |
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<script>
//로그인 submit
function join(){
var params = {
'userId' : $("#userId").val()
,'password' : $("#password").val()
,'userName' : $("#userName").val()
,'email' : $("#email").val()
,'handPhoneNo' : $("#handPhoneNo").val()
}
console.log(params);
$.ajax({
type : 'POST'
,url : '/auth/insertUser'
,dataType : 'json'
,data : params
,success : function(result) {
alert(result.resultMsg);
if(result.resultCode == '00'){
location.href="/auth/login";
}
},
error: function(request, status, error) {
}
})
}
</script>
login.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/loginForm.css"/>
<div class="wrapper fadeInDown">
<div id="formContent">
<!-- Tabs Titles -->
<!-- Icon -->
<div class="fadeIn first">
로그인
</div>
<!-- Login Form -->
<form>
<input type="text" id="loginId" class="fadeIn second" name="un" placeholder="아이디를 입력해주세요" onkeyup="enterkey()">
<input type="text" id="loginPw" class="fadeIn third" name="up" placeholder="비밀번호를 입력해주세요" onkeyup="enterkey()">
<input type="button" class="fadeIn fourth" value="Log In" onclick="loginSubmit()">
</form>
<!-- Remind Passowrd -->
<div id="formFooter">
<a class="underlineHover" href="/auth/join">회원가입</a>
</div>
</div>
</div>
<script>
//로그인 submit
function loginSubmit(){
var params = {
'un' : $.trim($("#loginId").val())
,'up' : $("#loginPw").val()
}
console.log(params);
if(params.un == ""){
alert("아이디를 입력해주새요");
return false;
}
else if(params.up == ""){
alert("비밀번호를 입력해주새요");
return false;
}
$.ajax({
type : 'POST'
,url : '/auth/login-proc'
,dataType : 'json'
,data : params
,success : function(result) {
console.log(result);
if(result.resultCode != "00"){
alert(result.resultMessage);
}
else{
location.href=result.targetUrl;
}
},
error: function(request, status, error) {
}
})
}
function enterkey(){
if (window.event.keyCode == 13) {
// 엔터키가 눌렸을 때
loginSubmit();
}
}
</script>
list-view.jsp |
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<form>
<table class="table table-hover">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>날짜</th>
<th>조회수</th>
</tr>
</thead>
<tbody>
<c:forEach var="board" items="${boardList.content}" varStatus="vs">
<tr>
<td>${resultDataTotal - (boardList.size * boardList.number) - vs.index}</td>
<td><a href='/board/update/${board.boardIdx}'>${board.boardTitle}</a></td>
<td>${board.regName}</td>
<td>
<fmt:parseDate value="${board.regDate}" var="regDate" pattern="yyyy-MM-dd HH:mm:ss.s"/>
<fmt:formatDate value="${board.regDate}" pattern="yyyy-MM-dd"/>
</td>
<td>${board.viewCount}</td>
</tr>
</c:forEach>
<c:if test="${resultDataTotal == 0}">
<tr>
<center><td colspan="6" style="text-align: center;">등록된 게시판 리스트가 없습니다.</td></center>
</tr>
</c:if>
</tbody>
</table>
<!-- ADMIN 권한일경우에만 글쓰기 권한있음 -->
<c:if test="${sessUserInfo.authority == 'ADMIN'}">
<div class="text-right">
<a href='/board/write' class="btn btn-primary">글쓰기</a>
</div>
</c:if>
<div class="text-center">
<c:if test="${resultDataTotal != 0}">
<ul class="pagination">
<paginator:print goPageScript="goPage" curPage="${boardList.number}" totPages="${boardList.totalPages}"/>
</ul>
</c:if>
<!--
<ul class="pagination">
<li><a href="#">prev</a></li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">next</a></li>
</ul>
-->
</div>
</form>
list.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<style>
/* paginate */
.pagination a.on {color:#fff;font-weight:600;background:#a0afbf;}
</style>
<body>
<div class="container">
<br>
<br>
<br>
<h1 class="page-header">게시판 목록</h1>
</br>
</br>
<div id="list-div">
</div>
</div>
</body>
-> 글 목록 jsp
list-js.jsp |
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<script>
$(document).ready(function(){
boardCtrl.init();
});
var boardCtrl = {
init : function(){
this.bindData();
this.bindEvent();
},
bindData : function(){
goPage(0);
},
bindEvent : function(){
}
}
//리스트 가져오기
function goPage(pageNum){
var size = "10";
var params = {
page : pageNum
,size : size
}
ajaxUtil.coGetAjaxPage(
"/main/ajax/list-view"
, params
, 'list-div'
, function(result){
}
);
}
var ajaxUtil = {
coGetAjaxPage : function(url, params, targetId, onLoadEvent) {
var request = $.ajax({
url : url,
async : false,
one : true,
type : "get",
traditional: true,
contentsType : "html",
data : params ? params : {}
});
request.done(function(result){
$("#" + targetId).empty().append($(result));
// 공통 UI 이벤트
util.bindUIEvent(targetId);
// custom UI 이벤트
if(onLoadEvent) {
onLoadEvent();
}
});
request.always(function(result){
});
request.fail(function(request,status,error){
if(request.status == 410) {
var node = document.createElement("div");
node.innerHTML = request.responseText;
var navigationNode = $("#err_msg", node);
var navigationHTML = navigationNode.val();
common.alert(navigationHTML);
// dimm 또는 팝업이 표시중인 경우 처리
if($(".popDim:visible").length > 0) {
$(".popDim").hide();
}
} else {
//console.log("페이지호출 실패")
}
});
}
}
</script>
update.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<body>
<article>
<div class="container" role="main">
<h2>게시판 상세</h2>
<form name="form" id="form" role="form" method="post" action="${pageContext.request.contextPath}/board/saveBoard">
<div class="mb-3">
<label for="title">제목</label>
<input type="text" class="form-control" id="boardTitle" name="boardTitle" placeholder="제목을 입력해 주세요" value="${boardInfo.boardTitle}" <c:if test="${sessUserInfo.authority != 'ADMIN'}">readonly</c:if>>
<input type="hidden" id="boardIdx" value="${boardIdx}" />
</div>
<div class="mb-3">
<label for="reg_id">작성자</label>
<input type="text" class="form-control" id="regId" name="regId" value="${boardInfo.regName}" readonly>
</div>
<div class="mb-3">
<label for="content">내용</label>
<textarea class="form-control" rows="5" id="boardContent" name="boardContent" placeholder="내용을 입력해 주세요" <c:if test="${sessUserInfo.authority != 'ADMIN'}">readonly</c:if>>${boardInfo.boardContent}</textarea>
</div>
</form>
<br>
<c:if test="${sessUserInfo.authority == 'ADMIN'}">
<div>
<button onclick="writeSubmit()" type="button" class="btn btn-sm btn-primary" id="btnSave">저장</button>
<button onclick="location.href='/'" type="button" class="btn btn-sm btn-primary" id="btnList">목록</button>
</div>
</c:if>
</div>
</article>
</body>
-> 글 수정 jsp
update-js.jsp |
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<script>
function writeSubmit(){
var params = {
boardTitle : $.trim($("#boardTitle").val())
,boardContent : $.trim($("#boardContent").val())
,userId : "${sessUserInfo.userId}"
,boardIdx : $("#boardIdx").val()
}
console.log(params);
if(params.boardTitle == ""){
alert("제목을 입력해주세요.");
return false;
}
else if(params.boardContent == ""){
alert("내용을 입력해주세요.");
return false;
}
$.ajax({
type : 'POST'
,url : '/board/update'
,dataType : 'json'
,data : JSON.stringify(params)
,contentType: 'application/json'
,success : function(result) {
alert("해당글이 정상적으로 수정되었습니다.");
location.href="/";
},
error: function(request, status, error) {
}
})
}
</script>
write.jsp |
<%@ page language="java" session="true" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ include file="/WEB-INF/template/constants.jsp"%>
<body>
<article>
<div class="container" role="main">
<h2>게시판 글쓰기</h2>
<form name="form" id="form" role="form" method="post" action="${pageContext.request.contextPath}/board/saveBoard">
<div class="mb-3">
<label for="title">제목</label>
<input type="text" class="form-control" id="boardTitle" name="boardTitle" placeholder="제목을 입력해 주세요">
</div>
<div class="mb-3">
<label for="reg_id">작성자</label>
<input type="text" class="form-control" id="regId" name="regId" value="${sessUserInfo.authorityNm}" readonly>
</div>
<div class="mb-3">
<label for="content">내용</label>
<textarea class="form-control" rows="5" id="boardContent" name="boardContent" placeholder="내용을 입력해 주세요"></textarea>
</div>
</form>
<br>
<div>
<button onclick="writeSubmit()" type="button" class="btn btn-sm btn-primary" id="btnSave">저장</button>
<button onclick="location.href='/'" type="button" class="btn btn-sm btn-primary" id="btnList">목록</button>
</div>
</div>
</article>
</body>
-> 글 등록 jsp
write-js.jsp |
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<script>
function writeSubmit(){
var params = {
boardTitle : $.trim($("#boardTitle").val())
,boardContent : $.trim($("#boardContent").val())
,userId : "${sessUserInfo.userId}"
}
console.log(params);
if(params.boardTitle == ""){
alert("제목을 입력해주세요.");
return false;
}
else if(params.boardContent == ""){
alert("내용을 입력해주세요.");
return false;
}
$.ajax({
type : 'POST'
,url : '/board/write'
,dataType : 'json'
,data : JSON.stringify(params)
,contentType: 'application/json'
,success : function(result) {
alert("해당글이 정상적으로 등록되었습니다.");
location.href="/";
},
error: function(request, status, error) {
}
})
}
</script>
tiles.xml |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<!-- 레이아웃 템플릿 정의 -->
<definition name="tiles/default/template" template="/WEB-INF/template/template.jsp">
<put-attribute name="meta" value="/WEB-INF/template/common/meta.jsp"/>
<put-attribute name="styles" value="/WEB-INF/template/common/styles.jsp"/>
<put-attribute name="scripts" value="/WEB-INF/template/common/scripts.jsp"/>
<put-attribute name="header" value="/WEB-INF/template/common/header.jsp"/>
<put-attribute name="snb" value="/WEB-INF/template/common/snb.jsp"/>
</definition>
<!-- Depth(4) : / A / B / C / D / E.jsp -->
<definition name="/tiles/view/*/*/*/*/*" extends="tiles/default/template">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}/{3}/{4}/{5}.jsp"/>
<put-attribute name="contents-js" value="/WEB-INF/views/{1}/{2}/{3}/{4}/{5}-js.jsp"/>
</definition>
<!-- Depth(3) : / A / B / C / D.jsp -->
<definition name="/tiles/view/*/*/*/*" extends="tiles/default/template">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}/{3}/{4}.jsp"/>
<put-attribute name="contents-js" value="/WEB-INF/views/{1}/{2}/{3}/{4}-js.jsp"/>
</definition>
<!-- Depth(2) : / A / B / C.jsp -->
<definition name="/tiles/view/*/*/*" extends="tiles/default/template">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}/{3}.jsp"/>
<put-attribute name="contents-js" value="/WEB-INF/views/{1}/{2}/{3}-js.jsp"/>
</definition>
<!-- Depth(1) : / A / B.jsp -->
<definition name="/tiles/view/*/*" extends="tiles/default/template">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}.jsp"/>
<put-attribute name="contents-js" value="/WEB-INF/views/{1}/{2}-js.jsp"/>
</definition>
<!-- Depth(0) : / A.jsp -->
<definition name="/tiles/view/*" extends="tiles/default/template">
<put-attribute name="contents" value="/WEB-INF/views/{1}.jsp"/>
<put-attribute name="contents-js" value="/WEB-INF/views/{1}-js.jsp"/>
</definition>
<!-- 화면 영역 분할 등에서 사용함 -->
<definition name="tiles/default/template-simple" template="/WEB-INF/template/template-simple.jsp">
</definition>
<!-- Depth(4) : / A / B / C / D / E.jsp -->
<definition name="/tiles/ajax/*/*/*/*/*" extends="tiles/default/template-simple">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}/{3}/{4}/{5}.jsp"/>
</definition>
<!-- Depth(3) : / A / B / C / D.jsp -->
<definition name="/tiles/ajax/*/*/*/*" extends="tiles/default/template-simple">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}/{3}/{4}.jsp"/>
</definition>
<!-- Depth(2) : / A / B / C.jsp -->
<definition name="/tiles/ajax/*/*/*" extends="tiles/default/template-simple">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}/{3}.jsp"/>
</definition>
<!-- Depth(1) : / A / B.jsp -->
<definition name="/tiles/ajax/*/*" extends="tiles/default/template-simple">
<put-attribute name="contents" value="/WEB-INF/views/{1}/{2}.jsp"/>
</definition>
<!-- Depth(0) : / A.jsp -->
<definition name="/tiles/ajax/*" extends="tiles/default/template-simple">
<put-attribute name="contents" value="/WEB-INF/views/{1}.jsp"/>
</definition>
<!-- 에러 페이지 레이아웃 -->
<definition name="tiles/error/template" template="/WEB-INF/template/error/error-template.jsp"/>
<definition name="/errors/*" extends="tiles/error/template">
<put-attribute name="contents" value="/WEB-INF/errors/{1}.jsp"/>
</definition>
<!-- login -->
<definition name="/tiles/single/auth/login" template="/WEB-INF/views/auth/login.jsp">
<put-attribute name="meta" value="/WEB-INF/template/common/meta.jsp" />
</definition>
</tiles-definitions>
build.gradle |
apply plugin: 'war'
def timestamp = new Date().format('yyMMddHHmm')
bootWar.enabled = false // (1)
war.enabled = true // (2)
war {
archiveName = "admin-${version}-"+timestamp+".war"
}
dependencies {
implementation project(':core')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation('org.springframework.security:spring-security-test')
//db
runtimeOnly 'mysql:mysql-connector-java'
// MyBatis
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.2")
testImplementation('org.springframework.boot:spring-boot-starter-test')
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools
implementation group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '2.2.6.RELEASE'
providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')
implementation group: 'org.springframework.security', name: 'spring-security-core', version: '5.3.0.RELEASE'
implementation group: 'org.springframework.security', name: 'spring-security-taglibs', version: '5.3.0.RELEASE'
// Apache Http Requests
implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.12' // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient
// Tiles Setting
implementation group: 'org.apache.tiles', name: 'tiles-jsp', version: '3.0.8' // https://mvnrepository.com/artifact/org.apache.tiles/tiles-jsp
implementation group: 'org.apache.tiles', name: 'tiles-core', version: '3.0.8' // https://mvnrepository.com/artifact/org.apache.tiles/tiles-core
/* Jsp Setting */
implementation group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.31' // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper
implementation group: 'org.apache.taglibs', name: 'taglibs-standard-impl', version: '1.2.5' // https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl
providedRuntime group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1' // https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api
implementation group: 'javax.servlet', name: 'jstl' // https://mvnrepository.com/artifact/javax.servlet/jstl
/* gson */
implementation 'com.google.code.gson:gson:2.8.6'
// https://mvnrepository.com/artifact/com.navercorp.lucy/lucy-xss-servlet
implementation group: 'com.navercorp.lucy', name: 'lucy-xss-servlet', version: '2.0.1'
}
다음으로는 core쪽 java 파일입니다. |
MainMapper.java |
package com.board.dao;
import java.util.List;
import java.util.Map;
import org.springframework.data.domain.Pageable;
import org.apache.ibatis.annotations.Param;
public interface MainMapper {
//글쓰기 리스트 조회
public List<Map<String, Object>> selectBoardList(
@Param(value = "params") Map<String, Object> params
,@Param(value = "pageable") Pageable pageable
,@Param(value = "size") int size);
//글쓰기 리스트 count
int selectBoardListCnt(@Param(value = "params") Map<String, Object> params);
//글쓰기 등록
public int insertBoard(Map<String, Object> params);
//글 수정
public int updateBoard(Map<String, Object> params);
//글쓰기 상세조회
public Map<String, Object> selectBoard(String boardIdx);
//조회수 업데이트
public int updateViewCount(Map<String, Object> params);
}
-> 메인관련 dao입니다.
UserMapper.java |
package com.board.dao;
import com.board.entity.UserInfo;
import com.board.entity.UserAuthority;
public interface UserMapper {
//사용자 정보 조회
UserInfo getUserInfo(String userId);
//사용자 권한 조회
UserAuthority getUserAuthorities(String userId);
//사용자 등록
public int insertUser(UserInfo userinfo);
//사용자 권한 등록
public int insertUserAuth(UserInfo userinfo);
//사용자 중복체크
int duplicateUserCheck(String userId);
}
-> 사용자 관련 dao입니다.
CamelMap.java |
package com.board.entity;
import org.apache.commons.collections4.map.ListOrderedMap;
import org.springframework.jdbc.support.JdbcUtils;
public class CamelMap extends ListOrderedMap<String, Object> {
private static final long serialVersionUID = 1L;
@Override
public Object put(String key, Object value) {
if (key.indexOf('_') < 0 && Character.isLowerCase(key.charAt(0)))
return super.put(key, value);
return super.put(JdbcUtils.convertUnderscoreNameToPropertyName((String) key), value);
}
}
-> xml에서 resultType를 camelMap으로 받기위해서 만들었습니다.
UserAuthority.java |
package com.board.entity;
import org.springframework.security.core.GrantedAuthority;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserAuthority implements GrantedAuthority {
/**
*
*/
private static final long serialVersionUID = 1L;
private String authority;
private String authorityNm;
@Override
public String getAuthority() {
// TODO Auto-generated method stub
return this.authority;
}
}
-> 사용자 권한 관련 vo
UserInfo.java |
package com.board.entity;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserInfo implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 1L;
//사용자 id
private String userId;
//사용자 이름
private String userName;
//패스워드
private String password;
//이메일
private String email;
//핸드폰 반호
private String handPhoneNo;
//사용여부
private String useYn;
//권한 list
public List<UserAuthority> authorities;
//권한
public String authority;
//권한 이름
public String authorityNm;
public String auth;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
-> 사용자 정보 관련 vo
LoginService.java |
package com.board.service;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.board.dao.UserMapper;
import com.board.entity.UserInfo;
@Service
public class LoginService {
@Autowired
UserMapper userMapper;
@Autowired
PasswordEncoder passwordEncoder;
public Map<String, Object> checkLoginInsert(UserInfo userInfo) {
Map<String, Object> result = new HashMap<String, Object>();
//0이면 중복되는 아이디 없음
int duplicateCheck = userMapper.duplicateUserCheck(userInfo.getUserId());
//신규등록
if(duplicateCheck == 0) {
//패스워드 bcrypt 암호화
String password = passwordEncoder.encode(userInfo.getPassword());
userInfo.setPassword(password);
//사용자 등록
userMapper.insertUser(userInfo);
result.put("resultCode", "00");
result.put("resultMsg", "정상적으로 회원이 등록되었습니다.");
//사용자 권한 등록
userMapper.insertUserAuth(userInfo);
}
//중복된 아이디가 있으므로 에러
else {
result.put("resultCode", "99");
result.put("resultMsg", "중복된 아이디가 있습니다. 아이디를 다시 입력해주세요.");
}
return result;
}
}
-> 로그인 서비스
MainService.java |
package com.board.service;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.board.dao.MainMapper;
@Service("mainService")
public class MainService {
@Autowired MainMapper mainMapper;
public Page<Map<String, Object>> selectBoardList(Map<String, Object> params, Pageable pageable, int size){
return new PageImpl<>(mainMapper.selectBoardList(params, pageable, size), pageable, mainMapper.selectBoardListCnt(params));
}
}
-> 메인 서비스
MainMapper.xml |
<?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">
<!-- FAQ SQL Mapper -->
<mapper namespace="com.board.dao.MainMapper">
<!-- 게시판 리스트 조회 -->
<select id="selectBoardList" resultType="CamelMap">
SELECT
TB.BOARD_IDX
,TB.BOARD_TITLE
,TB.BOARD_CONTENT
,TB.REG_ID
,TB.VIEW_COUNT
,TB.USE_YN
,TB.REG_DATE
,(SELECT TC.CODE_EXP FROM TB_CODE TC WHERE TC.CODE_NO = '200' AND TC.CODE_NAME = TA.AUTHORITY) AS REG_NAME
FROM
TB_BOARD TB
,TB_AUTHORITIES TA
WHERE
USE_YN = 'Y'
AND
TB.REG_ID = TA.USER_ID
<if test="pageable != null">
LIMIT #{pageable.offset}, #{size}
</if>
</select>
<!-- 게시판 count -->
<select id="selectBoardListCnt" resultType="int">
SELECT
COUNT(*)
FROM
TB_BOARD
WHERE
USE_YN = 'Y'
</select>
<!-- 게시판 상세 조회 -->
<select id="selectBoard" resultType="CamelMap">
SELECT
TB.BOARD_IDX
,TB.BOARD_TITLE
,TB.BOARD_CONTENT
,TB.REG_ID
,TB.VIEW_COUNT
,TB.USE_YN
,TB.REG_DATE
,(SELECT TC.CODE_EXP FROM TB_CODE TC WHERE TC.CODE_NO = '200' AND TC.CODE_NAME = TA.AUTHORITY) AS REG_NAME
FROM
TB_BOARD TB
,TB_AUTHORITIES TA
WHERE
USE_YN = 'Y'
AND
TB.REG_ID = TA.USER_ID
AND
TB.BOARD_IDX = #{boardIdx}
</select>
<!-- 게시판 등록 -->
<insert id="insertBoard">
INSERT INTO
TB_BOARD(
BOARD_TITLE
,BOARD_CONTENT
,REG_ID
,REG_DATE
)
VALUES(
#{boardTitle}
,#{boardContent}
,#{userId}
,NOW()
)
</insert>
<!-- 게시판 수정 -->
<update id="updateBoard">
UPDATE
TB_BOARD
SET
BOARD_TITLE = #{boardTitle}
,BOARD_CONTENT = #{boardContent}
WHERE
BOARD_IDX = #{boardIdx}
</update>
<!-- 조회수 업데이트 -->
<update id="updateViewCount">
UPDATE
TB_BOARD T1
SET
T1.VIEW_COUNT = #{viewCount}
WHERE
T1.BOARD_IDX = #{boardIdx}
</update>
</mapper>
-> 메인 xml
UserMapper.xml |
<?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">
<!-- FAQ SQL Mapper -->
<mapper namespace="com.board.dao.UserMapper">
<!-- 사용자 정보 조회 -->
<select id="getUserInfo" parameterType="String" resultType="UserInfo">
SELECT
USER_ID
,USER_NAME
,PASSWORD
,EMAIL
,HAND_PHONE_NO
,USE_YN
FROM
TB_USERS
WHERE
USER_ID = #{userId}
</select>
<!-- 사용자 권한 조회 -->
<select id="getUserAuthorities" parameterType="String" resultType="UserAuthority">
SELECT
TA.AUTHORITY
,(SELECT CODE_EXP FROM TB_CODE TCD WHERE TCD.CODE_NO = '200' AND TCD.CODE_NAME = TA.AUTHORITY) AS AUTHORITY_NM
FROM
TB_AUTHORITIES TA
WHERE
TA.USER_ID = #{userId}
</select>
<!-- 사용자 등록 -->
<insert id="insertUser" parameterType="UserInfo">
INSERT INTO
TB_USERS(
USER_ID
,PASSWORD
,USER_NAME
,EMAIL
,HAND_PHONE_NO
,USE_YN
)
VALUES(
#{userId}
,#{password}
,#{userName}
,#{email}
,#{handPhoneNo}
,'1'
)
</insert>
<!-- 사용자 중복체크 -->
<select id="duplicateUserCheck" parameterType="String" resultType="int">
SELECT
COUNT(*)
FROM
TB_USERS
WHERE
USER_ID = #{userId}
</select>
<!-- 사용자 권한 등록 -->
<insert id="insertUserAuth" parameterType="UserInfo">
INSERT INTO
TB_AUTHORITIES(
USER_ID
,AUTHORITY
)
VALUES(
#{userId}
,'USER'
)
</insert>
</mapper>
-> 사용자 xml
lucy-xss-servlet-filter-rule.xml |
<config xmlns="http://www.navercorp.com/lucy-xss-servlet">
<defenders>
<!-- XssPreventer 등록 -->
<defender>
<name>xssPreventerDefender</name>
<class>com.navercorp.lucy.security.xss.servletfilter.defender.XssPreventerDefender</class>
</defender>
<!-- XssSaxFilter 등록 -->
<defender>
<name>xssSaxFilterDefender</name>
<class>com.navercorp.lucy.security.xss.servletfilter.defender.XssSaxFilterDefender</class>
<init-param>
<param-value>lucy-xss-sax.xml</param-value> <!-- lucy-xss-filter의 sax용 설정파일 -->
<param-value>false</param-value> <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
</init-param>
</defender>
<!-- XssFilter 등록 -->
<defender>
<name>xssFilterDefender</name>
<class>com.navercorp.lucy.security.xss.servletfilter.defender.XssFilterDefender</class>
<init-param>
<param-value>lucy-xss.xml</param-value> <!-- lucy-xss-filter의 dom용 설정파일 -->
<param-value>false</param-value> <!-- 필터링된 코멘트를 남길지 여부, 성능 효율상 false 추천 -->
</init-param>
</defender>
</defenders>
<!-- default defender 선언, 필터링 시 지정한 defender가 없으면 여기 정의된 default defender를 사용해 필터링 한다. -->
<default>
<defender>xssPreventerDefender</defender>
</default>
</config>
-> xss 대응
build.gradle |
bootJar{
enabled = false;
}
jar {
enabled = true;
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
//security core
implementation group: 'org.springframework.security', name: 'spring-security-core', version: '5.3.1.RELEASE'
// https://mvnrepository.com/artifact/org.springframework/spring-jdbc
implementation 'org.springframework:spring-jdbc:5.3.9'
// https://mvnrepository.com/artifact/org.apache.commons/commons-collections4
implementation 'org.apache.commons:commons-collections4:4.4'
// https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons
implementation group: 'org.springframework.data', name: 'spring-data-commons', version: '2.5.2'
// https://mvnrepository.com/artifact/org.apache.ibatis/ibatis-core
implementation group: 'org.apache.ibatis', name: 'ibatis-core', version: '3.0'
}
게시판 화면 (로그인 안하였을경우) |
-> 로그인을 안하였을경우에는 게시판 글을 등록 및 수정을 할수없게 하였습니다.
게시판 화면 (로그인하였을경우) |
-> 글쓰기 화면이 보임
-> 회원가입, 로그인 -> 로그아웃으로 바뀜
게시판 글쓰기화면 |
게시판 수정화면 |
-> 다음 포스팅에서는 logback 개발 및 적용을 해보겠습니다. 감사합니다.
'스프링' 카테고리의 다른 글
[Spring Boot] 5. logback 설정 (Gradle+Mybatis+멀티프로젝트+MYSQL+STS) (0) | 2021.08.12 |
---|---|
[Spring] sts4에서 jsp 사용하기 (0) | 2021.08.12 |
[Spring Boot] 3. lombok 설정 + 로그인 + 회원가입 개발 (Gradle+Mybatis+멀티프로젝트+MYSQL+STS) (0) | 2021.08.05 |
[Spring Boot] 2. TILES 설정 (Gradle+Mybatis+멀티프로젝트+MYSQL+STS) (0) | 2021.08.03 |
[Spring Boot] 1. 멀티프로젝트 세팅 (Gradle+Mybatis+멀티프로젝트+MYSQL+STS) (4) | 2021.08.01 |