Spring의 다양한 기능을 활용한 간단한 글을 올리는 사이트
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
DataBinding, DataBuffering, DataValidation기능
request parameter 값을 form bean에 바인딩 해준다.
Data binding-aware tags를 사용하여 기존에 입력했던 값들이 지워지지 않게 한다.
@Controller
public class OfferController {
@Autowired
private OfferService offerService;
@RequestMapping("/offers")
public String showOffer(Model model) {
List<Offer> offers = offerService.getCurrent();
model.addAttribute("offers",offers);
return "offers";
}
@RequestMapping("/createoffer")
public String createOffer(Model model) {
//Data Buffering 을 위해 빈 model 인 Offer()를 만든다.
model.addAttribute("offer",new Offer());
return "createoffer";
}
@RequestMapping("/docreate")
public String doCreate(Model model, @Valid Offer offer, BindingResult result) {
if(result.hasErrors()) {
System.out.println("== Form data does not validated ==");
List<ObjectError> errors = result.getAllErrors();
for(ObjectError error:errors) {
System.out.println(error.getDefaultMessage());
}
return "createoffer"; //다시 돌려줌
}
//Form에서 넘어온 데이터를 Data Binding 시켜야 한다.
//Controller -> Service -> DAO
offerService.insert(offer);
System.out.println(offer);
return "offercreated";
}
}
createoffer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="sf" uri="https://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath }/resources/css/main.css">
</head>
<body>
<sf:form method="post" action="${pageContext.request.contextPath }/docreate"
modelAttribute ="offer">
<table class="formtable">
<tr>
<td class="label">Name :</td>
<td>
<sf:input class="control" type="text" path="name"/><br/>
<sf:errors path="name" class="error"/>
</td>
</tr>
<tr>
<td class="label">Email :</td>
<td>
<sf:input class="control" type="text" path="email"/><br/>
<sf:errors path="email" class="error"/>
</td>
</tr>
<tr>
<td class="label">Offer :</td>
<td>
<sf:textarea class="control" path="text" rows="10" cols="10"></sf:textarea><br/>
<sf:errors path="text" class="error"/>
</td>
</tr>
<tr>
<td class="label"></td>
<td><input type="submit" value="새 제안"></td>
</tr>
</table>
</sf:form>
</body>
</html>
Offer.java 입력 데이터에 대한 검증을 해준다.
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Offer {
private int id;
@Size(min=2, max=100, message="Name must be between 2 and 100 chars")
private String name;
@Email(message="Pelase provide a valid email address")
@NotEmpty(message="The email address cannot be empty")
private String email;
@Size(min=2, max=100, message="Name must be between 2 and 100 chars")
private String text;
}
<!-- Spring Security 5 / core, web, config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security-version}</version>
</dependency>
<!-- Spring security - DelegatingFilterProxy -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- filter 기능
TestFilter : url이 바뀔때 console에 찍어준다. // 추후 log를 사용해서 사용
//Servlet 앞단에서 처리해줌.
@WebFilter("/*")
public class TestFilter implements Filter {
public TestFilter() {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//방문 url 찍는 기능
System.out.println(((HttpServletRequest) request).getRequestURL());
// pass the request along the filter chain
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
- Spring Authentication, Authorization
메모리상, db상에서 회원에 대해서 인증하도록 설정
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/security"
xmlns:beans="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
https://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd">
<authentication-manager>
<!-- In Memory-->
<!--
<authentication-provider>
<user-service>
<user name="zeroco" authorities="ROLE_ADMIN"
password="{noop}letmein" />
<user name="alice" authorities="ROLE_USER"
password="{noop}letmein" />
</user-service>
</authentication-provider>
-->
<!-- In Database-->
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username, password, enabled from users where username =?"
authorities-by-username-query="select username, authority from authorities where username =?"/>
</authentication-provider>
</authentication-manager>
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/offers" access="permitAll" />
<intercept-url pattern="/createoffer" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/**" access="denyAll" />
<!-- login page -->
<form-login login-page="/login"
authentication-failure-url="/login?error" />
<!-- logout page -->
<logout/>
</http>
</beans:beans>
기존의 스프링에서 자동으로 만들어주는 log4j를 활용하지 않고, Logback을 사용하여 로그 기능을 활용한다.
단순히 ip주소와, 홈페이지 url에 대해서만 찍어주도록 하였다.
<!-- Logging -->
<!-- SLF4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<!-- 브리시 역할 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<!-- log4j를 logback으로 변경 -->
<!-- logback-classic만 넣어주면, core도 들어감 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>runtime</scope>
</dependency>
consoleAppender, RollingFileAppener를 사용
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Appender -->
<!-- console Appender -->
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>.%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %n
</Pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>TRACE</level>
</filter>
</appender>
<!-- dailyRollingFile Appender / 일별 단위로 rolling -->
<appender name="dailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>e:/tmp/rest-demo.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<FileNamePattern>rest-demo.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days' worth of history -->
<!-- 30일 까지 유지한다. -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg %n</Pattern>
</encoder>
</appender>
<!-- minuteRollingFile Appender / 분 단위로 rolling-->
<appender name="minuteRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover every minute -->
<FileNamePattern>e:/tmp/minutes/rest-demo-minute.%d{yyyy-MM-dd_HH-mm}.log</FileNamePattern>
<!-- keep 30 minutes' worth of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</Pattern>
</encoder>
</appender>
<logger name="kr.ac.hansung" additivity="false"><!-- additivity="false" 상속받지 않겠다. -->
<level value="DEBUG" />
<appender-ref ref="dailyRollingFileAppender"/>
<appender-ref ref="minuteRollingFileAppender"/>
<appender-ref ref="consoleAppender" />
</logger>
<root>
<level value="INFO" />
<appender-ref ref="consoleAppender" />
</root>
</configuration>
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
// private static final Logger logger =
// LoggerFactory.getLogger("kr.ac.hansung.controller.HomeController.java");
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletRequest request) {
// Trace -> Debug -> Info -> Warn -> Error
String url = request.getRequestURI().toString(); // url 찍기
String clientIPaddr = request.getRemoteAddr(); // client ip 주소 찍기
logger.info("Request URL : {}, Client IP: {}", url, clientIPaddr);
return "home";
}
}