반응형
반응형

@ModelAttribute에 들어갈 Class를 생성 후 별 다른 생성자를 만들지 않고,

그냥 사용하게 되면, 생성자를 찾을 수 없다는 오류가 발생한다.


이런 경우 비어 있는 기본 생성자를 하나 만들어 주면 해결이 된다.

반응형
,
반응형

freemarker같은 경우는 resources파일을 그냥 starter project구조에서

src/main/resources/templates 에만 넣어주면 알아서 설정을 다 해주는 구조였는데..


Boot에서는 가장 기본이 되는 jsp가 이리저리 해줄것이 많다.

이런걸 보면 Spring 에서도 jsp사용을 권장하고 있지는 않은 것 같다.


1. 의존성 주입 (pom.xml)


<dependency>
 <groupId>javax.servlet</groupId>
 <artifactId>jstl</artifactId>
</dependency>
<dependency>
 <groupId>org.apache.tomcat.embed</groupId>
 <artifactId>tomcat-embed-jasper</artifactId>
 <scope>provided</scope>
</dependency>



2. application.properties 변경 (javaConfig나 xml에서의 viewResolver 설정과 동일하니 별도로 설명은 안하겠음)

spring.view.prefix=/WEB-INF/views/   

spring.view.suffix=.jsp



위와 같은 설정이 되어 있어야, 컨트롤러가 jsp 페이지를 제대로 로딩한다.

jsp 쓸일은 없겠지만, Spring Security 예제 따라하다보니까 왜 안되는지 이해가 안되서 이틀동안 헤맨결과 이유가 저거였음...-_- 굳이 Boot에서는 jsp를 사용하지 않도록 하자.

반응형
,
반응형

Spring Security를 이용하는 페이지 작업 시 csrf 토큰을 넣기 위하여

아래와 같은 taglib을 사용해야 한다. (input hidden으로 박아넣겠다면 굳이 없어도 되겠지만..)


<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>



pom.xml에 의존성 주입을 jstl과 tomcat-embed-jasper만 넣어놓으면,

JasperException이 발생하며, tags를 가져오지 못했다고 뭐라뭐라 한다.


이런 경우 pom.xml에 아래와 같은 의존성을 주입해주면 해결이 된다.


<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
</dependency>



Spring Security가 제공해주는 Role의 신세계를 경험하기 위해 테스트를 하는데, 

이건 뭐 의존성이 장난이 아니다..;;

반응형
,
반응형

WebApplication.java 파일의 경우 Spring Boot의 Runner역할을 하고 있고,

@SpringBootApplication Annotation을 달게되면, ComponentScan등 기본적으로 되어야 하는 귀찮은 설정들이 해소가 된다.


그런데... 이 WebApplication.java 파일이 기본 package가 아닌 하위 package로 들어가게 되면

ComponentScan의 역할을 제대로 하지 않는 것 같다.


특히 JPA를 사용하는 경우 Repository를 제대로 찾지를 못해서 계속 bean 생성 오류를 뿜게 된다.


앞으로, WebApplication.java파일은 package root에 위치하도록 프로젝트를 구성해야 할 것 같다.

반응형
,
반응형

Spring Security는 기본적으로 UserDetailService를 구현한 인터페이스들을 이용해서 인증처리를 한다.


Default Jdbc인증 같은 경우 JdbcDaoImpl이라는 클래스가 인증의 역할을 담당하게 되는데,

Security를 통해 인증 후 principal이라는 객체에 인증 정보가 들어가는데, 이것도 사전에 정해진 객체인 org.springframework.security.core.userdetails.User라는 클래스에 맞게 들어가게 된다.


하지만, 서비스를 하다보면 저 객체에 포함되지 않는 정보들도 UI에 표시를 해줘야 하는 경우가 분명히 있다.


이런 경우 User클래스와 UserDetailService를 상속받아서 customizing을 하게 된다.


User클래스는 간단히 extends만 해서 새롭게 객체만 만들어주면 되고,

UserDetailService의 경우도 implements로 구현해야하는 메소드만 구현을 해주면 되는데,

보통은 그냥 이렇게 구현만 하면, Spring이 알아서 wrapping을 해주는게 정상인데, Spring Boot + Security의 경우 명시적으로 세팅을 해주어야 하는 부분이 있어서 공유함.


xml설정은 예전에 때려치웠기 때문에, java config기준으로 설명을 한다.


Security 설정을 위해서는 WebSecurityConfigurerAdapter라는 놈을 상속받아서 설정 파일을 만들고,

configureGlobal이라는 메소드를 bean으로 만들어주어야 한다. 


해당 메소드에 보면 AuthenticationManagerBuilder라는 객체가 Argument로 들어있는데, 이 값을 이용해서 아까 wrapping을 위해 만든

UserDetailService를 wrapping해줄 수가 있다.


주의할점 하나. User클래스 상속받아서 새거 만들 때, 추가되는 property들에 대해서 Getter, Setter를 만들어 주어야 함.


굳이 전체 소스 공개는 안하겠고, config 설정만 공개함.


//SecurityConfig.java
@Autowired
private UserDetailsService userDetailService;
 
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
 auth.userDetailsService(userDetailService);
}



UserDetailsService가 아무리해도 wrapping이 안되는 것 같길래... 이틀정도 삽질하다보니, config에 있었던 것임. 


반응형
,
반응형

pom.xml에 아래와 같이 boot-maven-plugin에 executable을 true로 설정해주면 된다.

war는 그냥 되지만, jar는 저 옵션이 있어야 데몬으로 뜨는듯


<build>
 <finalName>프로젝트명</finalName>
 <plugins>  
  <plugin>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-maven-plugin</artifactId>
   <configuration>
    <executable>true</executable>
   </configuration>
  </plugin>
 </plugins>
</build>



※ boot 1.3.3 부터는 true가 기본으로 붙는듯 하다. 따로 설정안해줘도 동작함;;
반응형
,
반응형

일단 Spring Boot App을 Tomcat에 올려서 사용하는 분들은 잘 될것이므로, 뒤로 가기를 눌러주시길..


Embedded Server로 띄우는 경우, 아래와 같은 구문을 freemarker상단에 넣어서 taglib을 가져오겠다고하면,

url이 정확한데도 불구하고, 찾을 수 없다고 나온다.


<#assign security=JspTaglibs["http://www.springframework.org/security/tags"] >



이런 경우 기본적으로 아래와 같은 두 가지의 라이브러리를 필요로 한다.


<dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <scope>provided</scope>
</dependency>
<dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
</dependency>



이렇게 추가를 하고, 돌려보면 jsp만 사용하는 환경에서는 잘 돌아갈 것이다.


하지만, Boot 환경에서의 Freemarker는 Spring Security의 TagLib에 대한 고려가 되어 있지 않기 때문에,

이미 설정된 부분을 불러와서 Tld를 로딩할 수 있도록 수동처리해주어야 한다.


아래와 같은 클래스를 만들어서 기존 Freemarker 설정(FreeMarkerConfigurer)을 불러와서, Tld를 직접 인식하도록 해준다.


import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

public class ClassPathTldsLoader  {
    
    private static final String SECURITY_TLD = "/META-INF/security.tld";
    
    final private List<String> classPathTlds;

    public ClassPathTldsLoader(String... classPathTlds) {
        super();
        if(classPathTlds.length == 0){
            this.classPathTlds = Arrays.asList(SECURITY_TLD);
        }else{
            this.classPathTlds = Arrays.asList(classPathTlds);
        }
    }

    @Autowired
    private FreeMarkerConfigurer freeMarkerConfigurer;
    
    @PostConstruct
    public void loadClassPathTlds() {
        freeMarkerConfigurer.getTaglibFactory().setClasspathTlds(classPathTlds);
    }
}


 


그 후, 아래와 같이 Bean으로 등록해주면 해결이 된다.


@Bean
@ConditionalOnMissingBean(ClassPathTldsLoader.class)
public ClassPathTldsLoader classPathTldsLoader(){
    return new ClassPathTldsLoader();
}


Spring Issue Tracker에는 2014년부터 문제가 되고 있던 사항(https://github.com/spring-projects/spring-boot/issues/907)

인데, 공식적으로 해결을 안해준다. ㅠㅠ

반응형
,
반응형

jsp는 <sec:csrfMetaTags /> 이거만 붙이면 됨..


freemarker는 <@sec.scrfMetaTags /> 이걸로 되야되는데... 안된다. tld파일에도 분명히 들어있는데.

freemarker를 참 싫어하는 듯


이럴 땐 아래처럼, 그냥 명시해준다.

어차피 View들어올때 csrf관련 객체가 같이 들어오기 때문에, 값 활용이 가능하다.


<meta name="_csrf_parameter" content="${_csrf.parameterName}" />
<meta name="_csrf_header" content="${_csrf.headerName}" />
<meta name="_csrf" content="${_csrf.token}" />



이제 form형식이 아닌, jquery로 ajax통신을 하여 데이터를 땡겨오는 경우에 한 번 더 문제가 발생한다.


http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/ 아래 링크를 참조해보면,

jquery 초기화 구문에, 아래 구문을 넣어서 이용하라고 한다. 

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
         xhr.setRequestHeader(header, token);
    });
});



다 맞는 말이긴한데, jquery 1.4버전 이하에서나 사용이 가능하고,

이후버전을 사용하면 뜬금없이 jquery가 toLowerCase()를 찾지 못했다고 나온다.


이런 경우! 아래와 같이 토큰과 헤더가 모두 있을 경우에만 처리하도록, 예외처리 구문을 넣으면 해결이 된다.

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        if(token && header) {
            xhr.setRequestHeader(header, token);
        }
    });
});



이후, 모든 $.ajax요청들에는 beforeSend구문에 csrf헤더 정보가 들어가게 되어,
ajax api 처리 시 csrf 토큰을 정상적으로 이용할 수 있게 된다.


반응형
,
반응형

Swagger SpringMvc가 어느 순간 SpringFox라는 이름으로 Swagger OpenAPI 2.0의 스펙을 가지고 새롭게 바뀌었다.

기존에는 Swagger ui도 github에서 다운받아서 어거지로 넣는 느낌이었지만, ui도 같이 maven에 올라가 있다.


SpringFox의 존재를 알게 된건, Legacy인 Swagger SpringMVC를 오랫만에 적용해보니, 2.0으로 업그레이드 하라는 문구가 나와서,

링크를 클릭해보니 이쪽으로 연결을 시켜주었다.


모든 것은 Spring Boot + Maven 기준임.


그럼 Migration작업을 시작해보자.


1. Maven 의존성 변경

 - 기존 (2013년을 마지막으로 더이상 버전업이 되지 않음)

<dependency>
    <groupId>com.mangofactory</groupId>
    <artifactId>swagger-springmvc</artifactId>
    <version>1.0.2</version>
</dependency>



 - 변경 (2016년 3월에 2.4버전이 나옴. 꾸준히 업데이트 중)

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.4.0</version>
    <scope>compile</scope>
</dependency>




2. Annotation이 변경됨

 - 기존 : @EnableSwagger

 - 변경 : @EnableSwagger2

 ※ 2.4 버전 기준으로 deprecated되었다는 warning이 발생하지만, 무시해도 됨.



3. 생성해야될 Bean이 달라짐.

 - 기존 : SpringSwaggerConfig를 Bean으로 불러와서, SwaggerSpringMvcPlugin을 생성하여 Bean으로 등록

          SwaggerUI에서 context-path가 두번 붙는 문제도 있어서, SwaggerPathProvider도 구현해서 넣어줘야 했었다.

          api 패턴도 정규식으로 넣어야 제대로 찾았었다.

예시 코드) 

@Autowired
private SpringSwaggerConfig springSwaggerConfig;

@Bean
public SwaggerSpringMvcPlugin swaggerSpringMvcPlugin() {
    return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
            .apiInfo(new ApiInfo("ApiInfo", null, null, null, null, null))
            .useDefaultResponseMessages(false)
            .includePatterns(".*api.*")
            .pathProvider(new SwaggerPathProvider() {               
                    @Override
                    protected String getDocumentationPath() {
                        return "/";
                    }
                    
                    @Override
                    protected String applicationPath() {
                        return "";
                    }
            });
}



 - 변경 : Docket이라는 애만 만들어서 Bean으로 등록

          api 패턴도 정규식이 아닌 누구나 알아볼 수 있도록 *.*로 바뀜 (*만 넣으면 못찾는 문제가 있음..)

예시 코드)

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.regex("/api/*.*"))             
            .build()
            .apiInfo(new ApiInfoBuilder().title("ApiInfo").build());
}




4. Swagger UI 

 - 기존 : https://github.com/swagger-api/swagger-ui 가서 git clone해다가 dist폴더명을 swagger로 바꾸어주고, src/main/resources/static 폴더에 옮겨주어야 한다. 

          접속주소 => http://ip:port/{context-path}/swagger/index.html

          favicon이 swagger의 icon으로 붙음.


 - 변경 : maven 의존성만 추가 (Spring boot가 아니라면 resource handling 설정이 필요함)

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.4.0</version>
    <scope>compile</scope>
</dependency>


          접속주소 => http://ip.port/{context-path}/swagger-ui.html

          favicon 없음 (아마 webapp의 favicon을 따라가지 않을까 생각됨)


아직 Spring 공식홈 Project목록에는 보이지 않으나, 사용하는 사람이 꽤 많아서 언젠간 생기지 않을까 싶음.

반응형
,
반응형

java webapp의 최대 단점인, java소스 수정 후 restart 해야 반영된다는 점이 spring-loaded라는 것을 사용하면 어느정도 해결이 된 듯 하다.


난 이제 무조건 Spring Boot를 사용하므로, Spring Boot위주로 설명한다.


maven을 사용하는 경우 plugin만 아래와 같이 추가를 하고, mvn spring-boot:run 으로 실행하면 된다.


<plugin>
    <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>springloaded</artifactId>
                <version>1.2.6.RELEASE</version>
            </dependency>
        </dependencies>
</plugin>



각 template 파일들은 caching이 되어 있으면, 기존 정보를 불러올 수 있으므로 아예 꺼버리도록 하자.

본인은 freemarker를 사용하므로, 아래와 같은 구문을 properties에 추가

spring.freemarker.cache=false


java 소스를 수정하고, ftl파일에서 뿌려보니 바로바로 적용이 잘 되는 것을 확인!

이제 돈내고 Jrebel 쓸 필요가 없어진 것 같다.


추가)

이클립스 또는 sts를 사용하는 경우 mvn spring-boot:run 으로 실행한 후, terminate로 shutdown을 시키면

javaw.exe 프로세스는 terminate되지만, java.exe 프로세스는 살아있어서, 강제로 kill 하기 전까지 다시 실행을 못하는 이슈가 있다.


이런 경우에 아래와 같이 configuration 설정을 추가 해준다.

(출처 : https://benlazaro.wordpress.com/2016/01/16/terminating-mvn-spring-bootrun-tomcat-from-eclipse/ )


<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>springloaded</artifactId>
                <version>1.2.6.RELEASE</version>
            </dependency>
        </dependencies>
    <configuration>
        <executable>true</executable>
        <fork>false</fork>
    </configuration>
</plugin>


반응형
,
반응형