visualkhh
3k
2016-12-06 15:45:26
8
16363

spring boot 정리 (locale, messageSource, security, jpa, hibernate, Scheduler, config)



안녕하세요


Spring boot  정리 해봅니다. 


https://github.com/visualkhh/lib-spring/tree/master/boot/default_template


visualkhh@gmail.com




springboot로 만든 기본틀입니다.


사용 : spring jpa, hibernate, scheduler, config, groovyTemplate


//설명은 소스안 주석으로 설명하겠습니다.








build.gradle

group 'com.boot'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
apply plugin: 'war'
apply plugin: 'groovy'
version = '1.0'
sourceCompatibility = 1.8
targetCompatibility = 1.8
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
compileJava {
options.fork = true
options.forkOptions.executable = "${System.getenv().JAVA_HOME}/bin/javac" // assumes that javac is on PATH
//options.compilerArgs << "-XDignore.symbol.file"// does not exist 오류나면..
}

buildscript {
ext {
springBootVersion = '1.4.1.RELEASE'
}
repositories {
mavenLocal()
mavenCentral()
}

dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
// classpath("org.springframework:springloaded:1.2.5.RELEASE")
}
}

repositories {
maven { url "http://119.206.205.172:8081/nexus/content/repositories/releases/" }
mavenCentral()
// maven { url "http://www.egovframe.go.kr/maven/" }
}


dependencies {
// egov 전자정부 프레임워크 감리통과하려면 아래 풀어 dependencies 걸어서 감리만 통과하여라
// compile(group: 'egovframework.rte', name: 'egovframework.rte.ptl.mvc', version:'3.5.0') {
// exclude(group: 'egovframework.rte')
//// exclude(module: 'commons-logging')
// }

compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-integration:${springBootVersion}")
//devtools
compile("org.springframework.boot:spring-boot-devtools")
//config를위하여
compile("org.springframework.boot:spring-boot-configuration-processor")
//optional ("org.springframework.boot:spring-boot-configuration-processor")

//view template select one!
compile("org.springframework.boot:spring-boot-starter-groovy-templates:${springBootVersion}")
//compile("org.springframework.boot:spring-boot-starter-thymeleaf:${springBootVersion}")
//compile("org.springframework.boot:spring-boot-starter-velocity:${springBootVersion}")
/*jsp jstl viwe 쓰려면
compile("javax.servlet:jstl");
compile("org.apache.tomcat.embed:tomcat-embed-jasper")
*/

compile("com.h2database:h2:1.4.192");

compile('com.jayway.jsonpath:json-path:2.1.0')

/* lombok*/
compile group: 'org.projectlombok', name: 'lombok', version: '1.16.8'
/* quartz */
compile group: 'org.quartz-scheduler', name: 'quartz', version: '1.8.6'
// https://mvnrepository.com/artifact/javax.transaction/jta
compile group: 'javax.transaction', name: 'jta', version: '1.1'

/* email */
compile group: 'javax.mail', name: 'mail', version: '1.4.7'

/* class scaner https://sites.google.com/site/javacornproject/corn-cps */
compile group: 'net.sf.corn', name: 'corn-cps', version: '1.1.7'


testCompile ("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")


compile group: 'com.omnicns', name: 'common-java', version: '0.0.1'
compile group: 'com.omnicns', name: 'common-jsp', version: '0.0.1'
compile group: 'com.omnicns', name: 'common-spring', version: '0.0.1'


testCompile group: 'junit', name: 'junit', version: '4.11'
}
compileJava.dependsOn(processResources)
idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
}
}




application.yml

#http://docs.spring.io/spring-boot/docs/current/reference/html/howto-properties-and-configuration.html
#http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
application:
version: 1.0
title: Board Application

spring:
devtools:
livereload:
enabled: true
freemarker:
cache: false
# templateLoaderPath:
config:
location:
application:
name: springboot
main:
banner-mode: off
h2:
console:
enabled: true # Enable the console.
path: /h2-console # Path at which the console will be available.
settings:
trace: false # Enable trace output.
web-allow-others: false # Enable remote access.
datasource:
name: testdb
initialize: true # Populate the database using 'data.sql'.
#schema:
#data:
platform: all # Platform to use in the schema resource (schema-${platform}.sql).
# url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driverClassName: org.h2.Driver
username: sa
password:
continue-on-error: true
# jpa:
# database-platform: org.hibernate.dialect.H2Dialect
# generate-ddl: true
# show-sql: true
# open-in-view: true
# hibernate:
# ddl-auto: create-drop
# properties:
# hibernate.current_session_context_class: thread
# hibernate.jdbc.batch_size: 50
# hibernate.format_sql: false
# hibernate.validator.apply_to_ddl: false
# hibernate.validator.autoregister_listeners: false
# javax.persistence.validation.mode: none
## hibernate.current_session_context_class: org.springframework.orm.hibernate5.SpringSessionContext
data:
repositories:
enabled: true
http:
multipart:
enabled: true # Enable support of multi-part uploads.
mvc:
locale: ko_KR
groovy:
template:
cache: false
# INTERNATIONALIZATION (MessageSourceAutoConfiguration)
messages:
# always-use-message-format: false # Set whether to always apply the MessageFormat rules, parsing even messages without arguments.
cache-seconds: -1 # Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles are cached forever.
basename: classpath:/messages/message # Comma-separated list of basenames, each following the ResourceBundle convention.
encoding: UTF-8 # Message bundles encoding.
# fallback-to-system-locale: true # Set whether to fall back to the system Locale if no files for a specific Locale have been found.



# SECURITY (SecurityProperties)
security:
basic:
authorize-mode: role # Security authorize mode to apply.
enabled: true # Enable basic authentication.
path: /** # Comma-separated list of paths to secure.
realm: Spring # HTTP basic realm name.
enable-csrf: true # Enable Cross Site Request Forgery support.
filter-order: 0 # Security filter chain order.
filter-dispatcher-types: ASYNC, FORWARD, INCLUDE, REQUEST # Security filter chain dispatcher types.
headers:
cache: true # Enable cache control HTTP headers.
content-type: true # Enable "X-Content-Type-Options" header.
frame: true # Enable "X-Frame-Options" header.
#hsts= # HTTP Strict Transport Security (HSTS) mode (none, domain, all).
xss: true # Enable cross site scripting (XSS) protection.
#ignored= # Comma-separated list of paths to exclude from the default secured paths.
require-ssl: false # Enable secure channel for all requests.
sessions: stateless # Session creation policy (always, never, if_required, stateless).
user:
name: user # Default admin name.
password: user # Password for the default admin name. A random password is logged on startup by default.
role: USER # Granted roles for the default admin name.
# view:
# prefix:
# suffix: :.vm

logging:
level:
root: INFO
org.springframework.web: INFO
org.hibernate: INFO
com.khh: INFO

multipart:
maxFileSize: 10Mb


######## CUSTOM PROPERTIES ##########
hibernate:
mapping-locations: classpath:hibernate/*.hbm.xml
# packages-to-scan: com.khh.project
# annotated-packages: com.khh.project
properties:
# hibernate.connection.url
hibernate.dialect: org.hibernate.dialect.H2Dialect
hibernate.show_sql: true
hibernate.hbm2ddl.auto: update
hibernate.current_session_context_class: thread

project:
properties:
hello: hello22






---

spring:
profiles: development

logging:
level:
root: INFO
org.springframework.web: INFO
org.hibernate: DEBUG
com.khh: DEBUG

---

spring:
profiles: production

logging:
#file: logs/application.log
level:
root: INFO
org.springframework.web: INFO
org.hibernate: INFO
com.khh: INFO







WebMvcConfigurerAdapter

1.다국어(locale)

2.messageSource

3.interceptor

 - springsecurity csrf

package com.khh.project.config.web;

import com.khh.project.web.error.ErrorController;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ModelAndView;
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.ViewControllerRegistry;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;


@Configuration
@EnableWebMvc
@Slf4j
public class WebMvcConfigurerAdapter extends org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter {


@Value("${spring.mvc.locale}")
Locale locale = null;

@Value("${spring.messages.basename}")
String messagesBasename = null;
@Value("${spring.messages.encoding}")
String messagesEncoding = null;
@Value("${spring.messages.cache-seconds}")
int messagesCacheSeconds;


@Autowired
SessionFactory sessionFactory = null;

// preHandle boolean 1. 클라이언트의 요청을 컨트롤러에 전달 하기 전에 호출
// false 인 경우 intercepter 또는 controller 를 실행 시키지 않고 요청 종료
// postHandle void 1. 컨트롤러 로직 실행 된 후 호출됨2. 컨트롤러 실행 도중 error 발생의 경우 postHandle() 는 실행 되지 않음
// request 로 넘어온 데이터 가공시 많이 쓰임
// afterCompletion void 1. 컨트롤러 로직 실행 된 후 호출 됨 2. 컨트롤러 실행 도중이나 view 페이지 실행 도중 error 발생 해도 실행됨
// 공통 Exception 처리 로직 작성시 많이 쓰임
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(csrfTokenAddingInterceptor()).addPathPatterns("/**"); //.includePathPatterns("/**") .excludePathPatterns("/**/*.ecxld");
registry.addInterceptor(localeChangeInterceptor()).addPathPatterns("/**");
registry.addInterceptor(sessionFactoryTransctionInterceptor()).addPathPatterns("/**");
}

@Bean
public HandlerInterceptor sessionFactoryTransctionInterceptor() {
return new HandlerInterceptorAdapter() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
sessionFactory.getCurrentSession().beginTransaction();
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
sessionFactory.getCurrentSession().getTransaction().commit();
}
};
}
@Bean
public HandlerInterceptor csrfTokenAddingInterceptor() {
return new HandlerInterceptorAdapter() {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView view) {
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (token != null && null != view) {
view.addObject(token.getParameterName(), token);
}
}
};
}



//다국어 https://justinrodenbostel.com/2014/05/13/part-4-internationalization-in-spring-boot/
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(locale);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}

//message source
@Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(messagesBasename); //"classpath:/messages/message"
messageSource.setDefaultEncoding(messagesEncoding);
messageSource.setCacheSeconds(messagesCacheSeconds);
return messageSource;
}

@Bean
public MessageSourceAccessor getMessageSourceAccessor(){
ReloadableResourceBundleMessageSource m = messageSource();
return new MessageSourceAccessor(m);
}


//로그인페이지.
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController(WebSecurityConfigurerAdapter.LOGIN_PAGE).setViewName("security/login");
}




@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> {
container.addErrorPages(
new ErrorPage(HttpStatus.UNAUTHORIZED, ErrorController.ERROR_401),
new ErrorPage(HttpStatus.FORBIDDEN, ErrorController.ERROR_403),
new ErrorPage(HttpStatus.NOT_FOUND, ErrorController.ERROR_404),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, ErrorController.ERROR_500),
new ErrorPage(Throwable.class, ErrorController.ERROR_DEFAULT)
);
};
}



@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resource/**") .addResourceLocations("/resource/");
registry.addResourceHandler("/static/**") .addResourceLocations("/static/");
registry.addResourceHandler("/static/**") .addResourceLocations("/static/");
registry.addResourceHandler("/img/**") .addResourceLocations("/img/");
registry.addResourceHandler("/image/**") .addResourceLocations("/image/");
}


}






spring jpa와 함께 hibernate original 사용하기위해 설정

package com.khh.project.config;

import com.khh.Application;
import com.khh.project.config.properties.HibernateProperties;
import com.khh.project.config.properties.ProjectProperties;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.devtools.classpath.ClassPathRestartStrategy;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.*;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;

@Configuration
@EnableTransactionManagement
@Slf4j
public class HibernateConfig {

@Autowired
DataSource dataSource;

@Autowired
HibernateProperties hibernateProperties;
@Autowired
ProjectProperties ProjectProperties;


@Autowired
private ResourceLoader resourceLoader;
//https://github.com/netgloo/spring-boot-samples/blob/master/spring-boot-mysql-hibernate/src/main/java/netgloo/configs/DatabaseConfig.java
@Bean
public LocalSessionFactoryBean sessionFactory() throws IOException {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setPackagesToScan();
factory.setDataSource(dataSource);
if (hibernateProperties.getMappingLocations() != null) {
ClassLoader cl = this.getClass().getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
Resource[] resources = resolver.getResources(hibernateProperties.getMappingLocations()) ;
for (Resource resource: resources){
factory.setMappingLocations(resources);
}
}
if (hibernateProperties.getPackagesToScan() != null) {
factory.setPackagesToScan(hibernateProperties.getPackagesToScan());
}else{
factory.setPackagesToScan(Application.BASE_PACKAGES);
}
if (hibernateProperties.getAnnotatedPackages() != null) {
factory.setAnnotatedPackages(hibernateProperties.getAnnotatedPackages());
}else{
factory.setAnnotatedPackages(Application.BASE_PACKAGES);
}
factory.setHibernateProperties(hibernateProperties.getProperties());
Properties hibernateProperties = new Properties();
SessionFactory sessionFactory = factory.getObject();
return factory;
}


} // class DatabaseConfig






SpringBootServletInitializer

- Scheduler, characterEncoding, War

package com.khh.project.config;

import com.khh.Application;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.Filter;
import java.nio.charset.Charset;

@Configuration
@EnableScheduling
public class SpringBootServletInitializer extends org.springframework.boot.web.support.SpringBootServletInitializer {
//war를 처리하기위해 필요하다.
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}


@Bean
public HttpMessageConverter<String> responseBodyConverter() {
return new StringHttpMessageConverter(Charset.forName("UTF-8"));
}

@Bean
public Filter characterEncodingFilter() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return characterEncodingFilter;
}


//////////////////////////scheduled 스케쥴 하기위하여//////////////////////
//////////////@EnableScheduling 붙쳐줘야한다
@Bean
public ScheduledExecutorFactoryBean scheduledExecutorService() {
ScheduledExecutorFactoryBean bean = new ScheduledExecutorFactoryBean();
bean.setPoolSize(5);
return bean;
}


}








WebSecurityConfigurerAdapter

- Security

package com.khh.project.config.web;

import com.khh.project.config.web.security.*;
import com.khh.project.config.web.security.user.LoginUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleHierarchyVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.http.SessionCreationPolicy;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

import static org.hibernate.criterion.Restrictions.and;


@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableWebSecurity
public class WebSecurityConfigurerAdapter extends org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter {

@Value("${spring.h2.console.enabled}")
boolean h2ConsoleEnabled;
@Value("${spring.h2.console.path}")
String h2ConsolePath;

public static final String ROOT_PATH = "/";
public static final String SECURITY_PATH = "/security";
public static final String ANON_PATH = "/anon";
public static final String AUTH_PATH = "/auth";

public static final String LOGIN_PAGE = SECURITY_PATH+"/login";
public static final String LOGIN_PROCESSING_URL = SECURITY_PATH+"/sign_in";
public static final String FAILURE_URL = SECURITY_PATH+"/fail";
public static final String USERNAME_PARAMETER = "username";
public static final String PASSWORD_PARAMETER = "password";
public static final String DEFAULT_SUCCESS_URL = ROOT_PATH;
public static final String LOGOUT_SUCCESS_URL = ROOT_PATH;
public static final String SESSION_EXPIRED_URL = LOGIN_PAGE+"?expred";
public static final String SESSION_INVALIDSESSION_URL = LOGIN_PAGE+"?invalid";
public static final String LOGOUT_URL = SECURITY_PATH+"/sign_out";
public static final String REMEMBER_ME_KEY = "REMEBMER_ME_KEY";
public static final String REMEMBER_ME_COOKE_NAME = "REMEMBER_ME_COOKE";



// @Autowired
// private LoginUserDetailsService userDetailsService;
@Autowired
AuthenticationManager authenticationManager;
// @Autowired
// private DataSource dataSource;


@Override
public void configure(WebSecurity web) throws Exception {
if(h2ConsoleEnabled) {
web.ignoring().antMatchers(h2ConsolePath+"/**");
}
web.ignoring().antMatchers("/resource/**","/static/**","/img/**","/image/**");
}


@Override
protected void configure(HttpSecurity http) throws Exception {
log.debug("-----security HttpSecurity-----"+http);
http
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/", ANON_PATH +"/**") .permitAll()
.antMatchers(AUTH_PATH +"/**") .hasRole("AUTH")
.antMatchers("/admin/**") .hasRole("ADMIN")
.antMatchers("/board/**") .hasRole("USER")
//.antMatchers("/board/**").hasAnyAuthority()
.anyRequest().authenticated()
.and()
.sessionManagement() //http://niees.tistory.com/17
.maximumSessions(1)
.expiredUrl(SESSION_EXPIRED_URL) //중복 로그인이 일어났을 경우 이동 할 주소​
.maxSessionsPreventsLogin(false) //만약 두번째 인증을 거부하게 하고 싶은 경우concurrency-control에​ error-if-maximum-exceeded="true"속성을 지정하면 된다.​
.and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl(SESSION_INVALIDSESSION_URL)
.and()
.formLogin()
.loginPage(LOGIN_PAGE) //로그인 페이지
.loginProcessingUrl(LOGIN_PROCESSING_URL) //login-processing-url 로그인 페이지 form action에 입력할 주소 지정
.failureUrl(FAILURE_URL) //실패시 이동될 페이지
.usernameParameter(USERNAME_PARAMETER)
.passwordParameter(PASSWORD_PARAMETER)
.defaultSuccessUrl(DEFAULT_SUCCESS_URL) //성공시 이동될 페이지
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.permitAll()
.and()
.rememberMe()
.key(REMEMBER_ME_KEY)
.rememberMeServices(tokenBasedRememberMeServices())
.and()
.logout()
.deleteCookies(REMEMBER_ME_COOKE_NAME)
.deleteCookies("JSESSIONID")
.logoutUrl(LOGOUT_URL)
.invalidateHttpSession(true)
.logoutSuccessUrl(LOGOUT_SUCCESS_URL)
// .logoutSuccessHandler(logoutSuccessHandler()) //커스텀으로 로그아웃된거에 대한 처리를 해주면 로그아웃성공URL로 가지 않으니 커스텀할떄 사용해여라
.logoutRequestMatcher(new AntPathRequestMatcher(LOGOUT_URL))
.permitAll()
.and()
.authenticationProvider(authenticationProvider()) //configure(AuthenticationManagerBuilder auth) 오버라이딩해서 추가할수도있다.
.csrf();
}



///////////////////////////AuthenticationProvider/////////////////////////////////////////////////
/**/ //AuthenticationProvider Interface는 자동으로 클래스패스에 있는거 찾아서 쓴다
/**/ //
/**/ // //DaoAuthenticationProvider daoAuthenticationProvider() 대신 이거 아래 처럼 등록을해도된다
/**/ // @Override
/**/ // protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**/ // auth.authenticationProvider(authenticationProvider());
/**/ // }
/**/ // @Bean //스프링에서 제공하는 기본적인 아이디 idpassword관련 처리
/**/ // public DaoAuthenticationProvider daoAuthenticationProvider() {
/**/ // DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
/**/ // daoAuthenticationProvider.setUserDetailsService(userDetailsService());
/**/ // daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
/**/ // daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
/**/ // return daoAuthenticationProvider;
/**/ // }
/**/ @Bean
/**/ public AuthenticationProvider authenticationProvider(){
/**/ return new AuthenticationProvider();
/**/ }
/////////////////////////////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////////////////////////////////
/**/ //커스텀 시큐리티 인텁셉터 하려면 아래를 사용하면된다
/**/ //사용전에 httpSecurity쪽에 추가해줘야된다 .addFilterBefore(filterSecurityInterceptor(), UsernamePasswordAuthenticationFilter.class)
/**/ ////////http://aoruqjfu.fun25.co.kr/index.php/post/657
/**/ @Bean
/**/ public FilterSecurityInterceptor filterSecurityInterceptor() {
/**/ FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
/**/ filterSecurityInterceptor.setAuthenticationManager(authenticationManager);
/**/ filterSecurityInterceptor.setSecurityMetadataSource(filterInvocationSecurityMetadataSource());
/**/ filterSecurityInterceptor.setAccessDecisionManager(affirmativeBased());
/**/ return filterSecurityInterceptor;
/**/ }
/**/ @Bean
/**/ public AffirmativeBased affirmativeBased() {
/**/ List<AccessDecisionVoter<? extends Object>> accessDecisionVoters = new ArrayList<>();
/**/ accessDecisionVoters.add(roleVoter());
/**/ AffirmativeBased affirmativeBased = new AffirmativeBased(accessDecisionVoters);
/**/ return affirmativeBased;
/**/ }
/**/ @Bean
/**/ public RoleHierarchyVoter roleVoter() {
/**/ RoleHierarchyVoter roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy());
/**/ roleHierarchyVoter.setRolePrefix("ROLE_");
/**/ return roleHierarchyVoter;
/**/ }
/**/ //RoleHierarchy 설정
/**/ @Bean
/**/ public RoleHierarchy roleHierarchy() {
/**/ RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
/**/ roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
/**/ return roleHierarchy;
/**/ }
/**/ //시큐리트쪽 부분에서 사용자가 화면 페이지 호출하면 매번 호출되는 클래스 중요함
/**/ @Bean
/**/ public FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource(){
/**/ return new FilterInvocationSecurityMetadataSource();
/**/ }
///////////////////////////////////////////////////////////////////////////////////////////////////






@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder();
}
@Bean
public LoginUserDetailsService userDetailsService(){
return new LoginUserDetailsService();
}
@Bean
public RememberMeServices tokenBasedRememberMeServices() {
TokenBasedRememberMeServices tokenBasedRememberMeServices = new TokenBasedRememberMeServices(REMEMBER_ME_KEY, userDetailsService());
tokenBasedRememberMeServices.setAlwaysRemember(true);
tokenBasedRememberMeServices.setTokenValiditySeconds(60 * 60 * 24 * 31);
tokenBasedRememberMeServices.setCookieName(REMEMBER_ME_COOKE_NAME);
return tokenBasedRememberMeServices;
}


//login,out 정상처리 및 실패에 대한 Bean
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
log.debug("#### login Success handler #####");
return new AuthenticationSuccessHandler();
}
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
log.debug("#### login Failurer handler #####");
return new AuthenticationFailureHandler();
}
//로그아웃 성공시 핸들링
@Bean
public LogoutSuccessHandler logoutSuccessHandler(){
return new LogoutSuccessHandler();
}


//

//REMEMBER ME를 위한.
// @Bean
// public PersistentTokenRepository persistentTokenRepository() {
// JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
// tokenRepositoryImpl.setDataSource(dataSource);
// return tokenRepositoryImpl;
// }

}




security groovyTemplate에서 csrf넣기 (interceptor설정부분)

login.tpl

//http://www.w3ii.com/ko/groovy/groovy_template_engines.html
layout 'layout.tpl', title: 'login',
content: contents {
div{
form(action:"${WebSecurityConfigurerAdapter.LOGIN_PROCESSING_URL}", method:'POST'){
input(type:'text', name:'username', value:'')
input(type:'password', name:'password', value:'')
input(type:'hidden', name:"${_csrf.parameterName}", value:"${_csrf.token}")
input(type:'submit', value:'submit')
}
}
}






UserDetailsService


package com.khh.project.config.web.security.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.core.userdetails.UsernameNotFoundException;


//@Transactional //http://jdm.kr/blog/141
public class LoginUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService{
@Autowired
MessageSourceAccessor messageSource;

@Autowired
LoginUserRepository userRepository;

@Override
public LoginUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LoginUserDetails userDetails = userRepository.findByUsername(username);
if (null == userDetails) {
throw new UsernameNotFoundException(messageSource.getMessage("error.login.fail"));
}
return userDetails;
}
}




AuthenticationProvider

- 로그인 처리


import com.khh.project.config.web.security.user.LoginUserDetails;
import com.khh.project.config.web.security.user.LoginUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
public class AuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider {

@Autowired
LoginUserDetailsService userDetailsService;
@Autowired
MessageSourceAccessor messageSource;

@Autowired
PasswordEncoder encoder;
@Override
// @Transactional //http://jdm.kr/blog/141
public Authentication authenticate(Authentication authentication) throws AuthenticationException {


WebAuthenticationDetails detail = (WebAuthenticationDetails) authentication.getDetails();
String remoteIP = detail.getRemoteAddress();
String username = (String)authentication.getPrincipal();
String password = (String)authentication.getCredentials();
LoginUserDetails userDetails = userDetailsService.loadUserByUsername(username);
log.info("Login try ip : -> "+remoteIP+" input id("+username+") info:"+userDetails);
if(null == userDetails || userDetails.isAccountNonLocked()==false || userDetails.isAccountNonExpired() == false || userDetails.isEnabled() == false || userDetails.isCredentialsNonExpired() == false) {
throw new UsernameNotFoundException(messageSource.getMessage("error.login.fail"));
}


if(!encoder.matches(encoder.encode(password),userDetails.getPassword())){ //실패
throw new BadCredentialsException(messageSource.getMessage("error.login.fail"));
}

UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities());
// token.setDetails(userDetails);
return token;
}

@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}




AuthenticationSuccessHandler

- 로그인성공시

package com.khh.project.config.web.security;

import com.khh.project.config.web.WebSecurityConfigurerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;



//로그인 성공 핸들러
@Slf4j
public class AuthenticationSuccessHandler implements org.springframework.security.web.authentication.AuthenticationSuccessHandler {

@Autowired
protected AuthenticationManager authenticationManager;
private RequestCache requestCache = null;
private RedirectStrategy redirectStrategy = null;

public AuthenticationSuccessHandler(){
requestCache = new HttpSessionRequestCache();
redirectStrategy = new DefaultRedirectStrategy();
}

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
sendRedirectDefaultUrl(request,response);
}

private void sendRedirectSessionUrl(HttpServletRequest request,HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
String targetUrl = savedRequest.getRedirectUrl();
redirectStrategy.sendRedirect(request, response, targetUrl);
}

private void sendRedirectRefererUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
String targetUrl = request.getHeader("REFERER");
redirectStrategy.sendRedirect(request, response, targetUrl);
}

private void sendRedirectDefaultUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
redirectStrategy.sendRedirect(request, response, WebSecurityConfigurerAdapter.DEFAULT_SUCCESS_URL);
}
}






LoginUserDetails

package com.khh.project.config.web.security.user;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;

@Entity
@Table(name = "USER")
@Data
public class LoginUserDetails implements org.springframework.security.core.userdetails.UserDetails, Serializable {

@Id
@Column(name = "USERNAME", unique = true, nullable = false)
private String username;

@Column(name = "PASSWORD", nullable = false)
private String password;

@Column(name = "ACCOUNTNONEXPIRED")
private boolean accountNonExpired;

@Column(name = "ACCOUNTNONLOCKED")
private boolean accountNonLocked;

@Column(name = "CREDENTIALSNONEXPIRED")
private boolean credentialsNonExpired;

@Column(name = "ENABLED")
private boolean enabled;


@OneToMany(fetch = FetchType.EAGER)
// @OneToMany
@JoinColumn(name="USERNAME")
private Collection<LoginUserGrantedAuthority> authorities;

}






LoginUserGrantedAuthority

package com.khh.project.config.web.security.user;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "AUTHORITY")
@Data
public class LoginUserGrantedAuthority implements org.springframework.security.core.GrantedAuthority {
@Id
@Column(name = "USERNAME")
private String username;

@Id
@Column(name = "AUTHORITY")
private String authority = null;

public LoginUserGrantedAuthority() {
}

public LoginUserGrantedAuthority(String authority) {
this.authority = authority;
}

}






SecurityController

package com.khh.project.web.security;

import com.khh.project.config.web.WebSecurityConfigurerAdapter;
import groovy.util.logging.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Controller
@RequestMapping(WebSecurityConfigurerAdapter.SECURITY_PATH)
public class SecurityController {


@RequestMapping({"","/"})
String index(HttpServletRequest request, HttpServletResponse response, Model model) {
return "security/login";
}



//수동로그아웃시킬때 사용하세요
public void logout(HttpServletRequest request, HttpServletResponse response){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
CookieClearingLogoutHandler cookieClearingLogoutHandler = new CookieClearingLogoutHandler(new String[]{"remember-me","JSESSIONID",WebSecurityConfigurerAdapter.REMEMBER_ME_COOKE_NAME});
SecurityContextLogoutHandler securityContextLogoutHandler = new SecurityContextLogoutHandler();
cookieClearingLogoutHandler.logout(request, response, (Authentication)null);
securityContextLogoutHandler.logout(request, response, (Authentication)null);
request.getSession().invalidate();
}
}


}










@ControllerAdvice

 - error Controller


@org.springframework.web.bind.annotation.ControllerAdvice(Application.BASE_PACKAGES)
@Slf4j
public class ControllerAdvice {


@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, EntityNotFoundException exception){
log(request,response,exception);
//log.trace(exception.getMessage(),exception);
ModelAndView mav = new ModelAndView();
response.setHeader("x-status", "Exception");
mav.addObject("throwable", exception);
mav.setViewName("error/default");
return mav;
}


@ExceptionHandler({UsernameNotFoundException.class})
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseBody
ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, UsernameNotFoundException exception){
log(request,response,exception);
//log.trace(exception.getMessage(),exception);
ModelAndView mav = new ModelAndView();
response.setHeader("x-status", "Exception");
mav.addObject("throwable", exception);
mav.setViewName("error/default");
return mav;
}



@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, Throwable exception){
log(request,response,exception);
//log.trace(exception.getMessage(),exception);
ModelAndView mav = new ModelAndView();
response.setHeader("x-status", "Exception");
mav.addObject("throwable", exception);
mav.setViewName("error/default");
return mav;
}

private void log(HttpServletRequest request, HttpServletResponse response, Throwable ex) {
Throwable tex = StackTraceUtil.getLastCause(ex);
StackTraceElement[] stacks = tex.getStackTrace();

StringBuffer logInfo = new StringBuffer();
logInfo.append(ex.getClass().getName());
logInfo.append("|").append(request.getRequestURI());//API CODE(URL)
logInfo.append("|").append(RequestUtil.getRemoteAddr(request)); // 접속IP
logInfo.append("|").append(null==ex.getMessage()?"":ex.getMessage().replace("\n","").replace("\r", "")); // 처리 담당자 ID
logInfo.append("|").append(Stream.of(stacks).map(at->at.toString()).collect(Collectors.joining("<<")));
log.error(logInfo.toString().replace("\n", "").replace("\r",""));
}
}




schduler

@Component
@Slf4j
public class Scheduler {

// 매일 5시 30분 0초에 실행한다.
@Scheduled(cron = "0 30 5 * * *")
public void aJob() {
// 실행될 로직
}

// 매월 1일 0시 0분 0초에 실행한다.
@Scheduled(cron = "0 0 0 1 * *")
public void anotherJob() {
// 실행될 로직
}

@Scheduled(cron = "0/10 * * * * ?")
public void anotherJob2() {
log.debug(new Date().toString());
}
}






ErrorController



@Controller
@Slf4j
public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController {

public static final String ERROR_PATH = "/error";
public static final String ERROR_DEFAULT = ERROR_PATH + "/default";
public static final String ERROR_401 = ERROR_PATH + "/401";
public static final String ERROR_403 = ERROR_PATH + "/403";
public static final String ERROR_404 = ERROR_PATH + "/404";
public static final String ERROR_500 = ERROR_PATH + "/500";



@RequestMapping(value = ERROR_DEFAULT, method = GET)
public String defaultError() {
return "error/default";
}

@RequestMapping(value = ERROR_401, method = GET)
public String error401() {
return "error/401";
}

@RequestMapping(value = ERROR_403, method = GET)
public String error403() {
return "error/403";
}

@RequestMapping(value = ERROR_404, method = GET)
public String error404() {
return "error/404";
}

@RequestMapping(value = ERROR_500, method = GET)
public String error500() {
return "error/500";
}

@Override
public String getErrorPath() {
return ERROR_DEFAULT;
}
}






hibernate, springJPA

package com.khh.project.web.admin;

import com.khh.project.web.admin.domain.Authority;
import com.khh.project.web.admin.domain.User;
import com.khh.project.web.admin.repository.UserRepository;
import com.omnicns.java.db.hibernate.Hibernater;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/admin")
@Transactional
@javax.transaction.Transactional(rollbackOn = { Exception.class })
@Slf4j
public class AdminController {


@Autowired
public SessionFactory sessionFactory;

@Autowired
public UserRepository userRepository;


@RequestMapping({"","/"})
@ResponseBody
String home() {
return "admin main";
}



@RequestMapping("/save")
@ResponseBody
String save() {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.beginTransaction();
User u = new User();
u.setUsername("zzz");
u.setPassword("zzz");

Authority a = new Authority();
a.setUsername("zzz");
a.setAuthority("vvv");
List l= new ArrayList<>();
l.add(a);
u.setAuthorities(l);

currentSession.save(u);

currentSession.flush();
currentSession.clear();
currentSession.getTransaction().commit();

return "ok";
}



@RequestMapping("/list")
@ResponseBody
List<User> list() {
Session currentSession = sessionFactory.getCurrentSession();

// currentSession.beginTransaction();
Criteria crit = currentSession.createCriteria(User.class);
// crit.setFetchMode("authorities", FetchMode.JOIN); //강제로 지연로딩 사용안함
//lazy로되어있다면 아래처럼 DISTINCT_ROOT_ENTITY를하던가 트랜젝션을 닫지말고 그냥 흘려보네 그냥 지연로딩을 사용해도된다. 지연로딩 사용하면 각각 쿼리날라간다
////crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);//조인 쿼리가 날라간다
//위처럼할수도 있지만 엔티티에도 넣어도된다 @Fetch(FetchMode.SELECT) 이것도 각각 날라간다
List<User> list = crit.list();
// currentSession.flush();
// currentSession.clear();
// currentSession.getTransaction().commit();

// List<User> list = (List<User>) crit.uniqueResult();
// currentSession.getTransaction().commit();
// currentSession.close();
return list;
}

@RequestMapping("/list2")
@ResponseBody
List<User> list2() {
List<User> list = userRepository.findAll();
return list;
}
@RequestMapping("/list3")
@ResponseBody
User list3() {
User list = userRepository.findByUsername("khh");
return list;
}





}












Application

package com.khh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableAutoConfiguration
@SpringBootApplication(scanBasePackages = {Application.BASE_PACKAGES})
@EnableTransactionManagement
public class Application {
public static final String BASE_PACKAGES = "com.khh.project";

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}

}












spring boot로 프로젝트 시작하기전에 참고하셔서 사용하셨으면 좋겠습니다.


먼저 삽질한거 공유합니다. 배워서 남주자~


ps: libqa 참고 하였습니다.  퀵형~ 고마워욤~


libqa github : https://github.com/visualkhh/libqa


springboot template github : https://github.com/visualkhh/lib-spring/tree/master/boot/default_template




visualkhh@gmail.com  감사합니다.




24
22
  • 댓글 8

  • 꾜뀨꺄
    231
    2016-12-08 10:51:53

    고맙습니다. 잘 참고하겠습니다.

    0
  • 훈마로
    378
    2016-12-08 15:07:42

    감사합니다

    0
  • 손이시렵다
    1k
    2016-12-08 22:17:37

    정말 깔끔하게 잘 만드셨네요

    많은 분들이 유용하게 사용하실수 있을거같습니다


    0
  • 진C
    1k
    2016-12-10 13:15:45
    큰 도움 너무나 고맙습니다! 좋은 날들 되세요~!
    0
  • ksw
    29
    2016-12-27 18:08:26

    와...정리 정말...멋지게 잘되어있네요 ㄷㄷ 

    0
  • visualkhh
    3k
    2017-03-28 15:28:35

    EntityManagerFectoryBuilder를 가지고  SessionFactory 추출하는 방법입니다.



    //////////////////hibernate
    @Bean(name="primarySessionFactory")
    @Primary
    public SessionFactory primarySessionFactory(EntityManagerFactoryBuilder builder) throws NamingException {
    return primaryEntityManagerFactory(builder).getNativeEntityManagerFactory().unwrap(SessionFactory.class);
    }
    @Bean(name="innerSessionFactory")
    public SessionFactory innerSessionFactory(EntityManagerFactoryBuilder builder) throws NamingException {
    return innerEntityManagerFactory(builder).getNativeEntityManagerFactory().unwrap(SessionFactory.class);
    }



    0
  • 진C
    1k
    2017-03-30 21:01:51

    배워서 남주자.....

    제목이 이상해서 들어가보면 visualkhh 님 글...

    많이 배우고 있습니다. 고맙습니다!

    0
  • EastGlow
    1k
    2019-06-14 15:20:52

    스프링 부트 쪽에서는 다국어 메시지 처리 설정이 어떻게 되어 있나 공부하던 중에 좋은 글 보게 되었네요. 잘 보고 갑니다^^

    1
  • 로그인을 하시면 댓글을 등록할 수 있습니다.