반응형

Spring Boot 환경에서 Spring security를 사용하고 있습니다.


원래 ...headers().frameOptions().sameOrigin() 을 기본으로 전체 적용해서 사용하고 있었습니다.


그런데 외부에 iframe에 들어가야하는 페이지가 하나 추가적으로 필요하게 되었습니다.


그냥 url 1개만 X-Frame-Options 헤더를 제거하면 되는데 이게 생각처럼 쉽지가 않았습니다.


아무리 구글링해도 명쾌한 해답이 없었습니다.


무수한 삽질 끝에 regexMatcher 를 이용해 URL 기반으로 Spring security 설정을 분리할 수 있었습니다.


/link/iframe 이라는 URL 만 X-Frame-Options 헤더를 제거하는 코드 샘플입니다.

@EnableWebSecurity
@Configuration
public class DefaultHttpSecurityConfig extends WebSecurityConfigurerAdapter {
  ...
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/link/iframe")//
        // ---------------
        .authorizeRequests()//
        //
        .antMatchers("/link/iframe").permitAll()//   
        .and().headers().frameOptions().disable();
 
    http.regexMatcher("^((?!" + "/WEB-INF/jsp/link/iframe.jsp" + ").)*$")
        // ---------------
        .authorizeRequests()//
        //
        .antMatchers(//
            ...
        ).permitAll()//
 
        .anyRequest().authenticated()//
        .and().headers().frameOptions().sameOrigin()//
        ...
    // ---------------
    ;
 
    ...
   
  }
 
  ...
}


특이사항으로 regexMatcher는 url이 아닌 view 실제 경로로 비교를 합니다.


org.springframework.security.web.util.matcher.RegexRequestMatcher 클래서의 matches 메서드에서 확인할 수 있습니다.


당연히 URL 비교로 생각하고 이 부분에서 삽질을 많이 했네요. ( 도대체 왜 view파일로 비교하는거지 ㅡㅡ? )


반응형
반응형

결론 부터 말씀 드리면 "Mobi" 가 User agent에 포함되어 있으면 모바일 입니다.

 

 
 

 

------------------------------------------------------------------------------------

모바일, 태블릿 혹은 데스크탑섹션

The most common reason to perform user agent sniffing is to determine which type of device the browser runs on. The goal is to serve different HTML to different device types.

  • Never assume that a browser or a rendering engine only runs on one type of device. Especially don't make different defaults for different browsers or rendering engines.
  • Never use the OS token to define if a browser is on mobile, tablet or desktop. The OS may run on more than one type of (for example, Android runs on tablets as well as phones).

The following table summarizes the way major browser vendors indicate that their browsers are running on a mobile device:

Common browsers User Agent strings
브라우저 규칙 예제
Mozilla (Gecko, Firefox) Mobile or Tablet token in the comment. Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0
WebKit-based (Android, Safari) Mobile Safari tokenoutside the comment. Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
Blink-based (Chromium, Google Chrome, Opera 15+) Mobile Safari tokenoutside the comment Mozilla/5.0 (Linux; Android 4.4.2); Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Mobile Safari/537.36 OPR/20.0.1396.72047
Presto-based (Opera 12-)

Opera Mobi/xyz tokenin the comment (Opera 12-)

Opera/9.80 (Android 2.3.3; Linux; Opera Mobi/ADR-1111101157; U; es-ES) Presto/2.9.201 Version/11.50

Internet Explorer IEMobile/xyz token in the comment. Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)

In summary, we recommend looking for the string “Mobi” anywhere in the User Agent to detect a mobile device.

If the device is large enough that it's not marked with “Mobi”, you should serve your desktop site (which, as a best practice, should support touch input anyway, as more desktop machines are appearing with touchscreens).

 

 

참고 URL : https://developer.mozilla.org/ko/docs/Web/HTTP/User_agent%EB%A5%BC_%EC%9D%B4%EC%9A%A9%ED%95%9C_%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80_%EA%B0%90%EC%A7%80#%EB%AA%A8%EB%B0%94%EC%9D%BC_%ED%83%9C%EB%B8%94%EB%A6%BF_%ED%98%B9%EC%9D%80_%EB%8D%B0%EC%8A%A4%ED%81%AC%ED%83%91

반응형

'스크립트' 카테고리의 다른 글

javascript object length 구하기.  (0) 2019.06.30
css - 줄바꿈  (0) 2019.05.15
vim - 하이라이트 끄기  (0) 2019.05.13
IE img label for 클릭 안될때 처리  (4) 2019.03.27
html5 javascript 이미지 미리보기  (0) 2018.11.29
cmd process kill  (0) 2018.11.17
javascript padStart - left padding  (0) 2018.11.07
curl 기본 사용법  (0) 2018.11.07
반응형
 

 

반응형
반응형

Spring boot 에서 사용자가 정의한 favicon을 사용하기위한 방법입니다.


application.properties

spring.mvc.favicon.enabled=false


반응형
반응형

java.lang.IllegalStateException: No Java compiler available for configuration options compilerClassName: [null] and compiler: [null]


오류 발생시에 아래 라이브러리 추가.


        <!-- Add this -->
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.6.1</version>
            <scope>provided</scope>
        </dependency>


* Spring-boot 2.1.3.RELEASE 에서 발생

반응형
반응형

mybatis spring boot starter 를 이용하고 있습니다.

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.2</version>
    </dependency>


보통은 application.properites 에서 아래와 같이 설정해서 사용하고 있습니다.

mybatis.config-location=classpath:config/mybatis-config.xml


config/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- <properties resource="config/jdbc.properties" /> -->
  <settings>
    <setting name="cacheEnabled" value="true" />
    <setting name="useGeneratedKeys" value="true" />
    <setting name="defaultExecutorType" value="REUSE" />
    <setting name="defaultStatementTimeout" value="3000" />
  </settings>
  <typeAliases>
    <package name="goni9071.plugin.member.entity" />
    <package name="goni9071.core.entity" />
  </typeAliases>
  <mappers>
    <mapper resource="core/sql/a.xml" />
    <mapper resource="core/sql/b.xml" />
    <mapper resource="core/sql/c.xml" />
    <mapper resource="plugin/member/sql/d.xml" />
    <mapper resource="plugin/member/sql/e.xml" />
    <mapper resource="plugin/member/sql/f.xml" />
  </mappers>
 
</configuration>


개발을 하다보면 프로젝트를 여러개로 나누어서 해야하는 경우도 있습니다.

그리고 쿼리들도 서로 의존성 없이 나누어져서 사용해야할 수도 있습니다.

multiple datasource 이야기는 아닙니다.


one datasource 에서 독릭접으로 프로젝트를 구성하고 mybatis도 따로 구성하고 싶은 이야기입니다.

하지만 one datasource 에서 Connection pool을 별도로 사용하고 싶지는 않습니다.


One Datasource + One Connection Pool + Multiple Configuration 이 정도 인것 같습니다.


아무리 찾아봐도 mybatis-config.xml 을 여러개로 나누어서 사용하는 방법은 없는 것 같았습니다.


하지만 mybatis-config.xml 을 Java Config로 변경하고 Java Config를 여러개로 나누는 것은 가능했습니다.



application.properties

mybatis.configuration.cache-enabled=true
mybatis.configuration.use-generated-keys=true
mybatis.configuration.default-executor-type=reuse
mybatis.configuration.default-statement-timeout=3000



Core 프로젝트

 
import java.io.InputStream;
 
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.Resources;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class CorerMybatisConfig {
  private static Logger logger = LoggerFactory.getLogger(CorerMybatisConfig.class);
 
  @Bean
  ConfigurationCustomizer coreMybatisConfigurationCustomizer() {
    return new ConfigurationCustomizer() {
      @Override
      public void customize(org.apache.ibatis.session.Configuration configuration) {
        try {
          configuration.getTypeAliasRegistry().registerAliases("goni9071.core.entity");
          String[] mapperResources = { "core/sql/a.xml""core/sql/b.xml""core/sql/c.xml" };
          for (String resource : mapperResources) {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            ErrorContext.instance().resource(resource);
            mapperParser.parse();
          }
        } catch (Exception e) {
          logger.error("Mybatis Loading Error", e);
        }
      }
 
    };
  }
}


Plugin Member 프로젝트

 
import java.io.InputStream;
 
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.Resources;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class PluginMemberMybatisConfig {
  private static Logger logger = LoggerFactory.getLogger(CorerMybatisConfig.class);
 
  @Bean
  ConfigurationCustomizer coreMybatisConfigurationCustomizer() {
    return new ConfigurationCustomizer() {
      @Override
      public void customize(org.apache.ibatis.session.Configuration configuration) {
        try {
          configuration.getTypeAliasRegistry().registerAliases("goni9071.plugin.member.entity");
          String[] mapperResources = { "plugin/member/sql/d.xml""plugin/member/sql/e.xml""plugin/member/sql/f.xml" };
          for (String resource : mapperResources) {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            ErrorContext.instance().resource(resource);
            mapperParser.parse();
          }
        } catch (Exception e) {
          logger.error("Mybatis Loading Error", e);
        }
      }
 
    };
  }
}



주의할점은 아래 코드에서 보이는 것 처럼 ConfigurationCustomizer 가 실행되려면 application.properites 에 mybatis.configuration 가 하나 이상있어야 합니다. 그리고 mybatis.config-location 가 있어도 안됩니다.


org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

...
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
...


반응형
반응형

쓸때마다 헤메는 HttpServletRequest  method 정리입니다.

 

요청 URL : http://localhost:8090/jam/abc?type=spider

 

 

 

반응형
반응형


마땅한 erd 툴이 없을 경우에 아래처럼 엑셀에다가 명세를 작성하기도 합니다.

그리고 아래 형식을 그대로 복사해서 테이블 생성을 하고 싶기도 합니다.

그래서 한번 만들어 보았습니다.



<코드>

<textarea style="width:100%;height:200px;" id="a"></textarea>
 
<textarea style="width:100%;height:200px;" id="b"></textarea>
 
<script>
$(document).ready(function() {
  $(document).on("keyup""#a", generateMysqlCrateTable);
});
 
function generateMysqlCrateTable() {
  var source = $("#a").val();
  var lines = source.split("\n");
  var result = "";
  var pks = [];
  var tableComment = "";
  for (var i = 0; i < lines.length; i++) {
    switch (i) {
      case 1:
        continue;
        break;
      case 0:
        var words = lines[i].split("\t");
        result += "CREATE TABLE `" + words[1+ "` (";
        tableComment = words[0];
        break;
      default:
        var words = lines[i].split("\t");
        result += "  `" + words[1+ "` ";
        if (words[2!= '') {
          pks.push(words[1]);
        }
        result += " " + words[5];
        if (words[6!= '') {
          result += "(" + words[6+ ")";
        }
        if (words[4!= '') {
          result += " NOT NULL ";
        }
        if (words[3!= '') {
          result += " AUTO_INCREMENT ";
        }
 
        if (words[7!= '') {
          result += " DEFAULT " + words[7];
        }
        if (words[0!= '') {
          result += " COMMENT '" + words[0+ "'";
        }
        result += ",";
        break;
    }
    result += "\n";
  }
  if (pks.length > 0) {
    result += " PRIMARY KEY (";
    for (var i in pks) {
      result += "`" + pks[i] + "`,";
    }
    result = result.slice(0-1);
 
    result += "),\n";
  }
 
  result = result.slice(0-2+ "\n";
 
  result += ") ENGINE=InnoDB COMMENT='" + tableComment + "'; "
 
  $("#b").text(result);
  return;
}
 
<script>



반응형

'UTIL' 카테고리의 다른 글

javascript - snake to camel  (0) 2019.02.17
반응형
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
 
import com.google.common.collect.Lists;
 
public class ZipUtil {
  public static void zip(String zipFileName, List<File> files) throws IOException {
    byte[] buf = new byte[1024];
 
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
    try {
 
      for (int i = 0; i < files.size(); i++) {
        FileInputStream in = new FileInputStream(files.get(i));
        try {
          String fileName = files.get(i).getAbsolutePath();
 
          ZipEntry ze = new ZipEntry(fileName);
          out.putNextEntry(ze);
 
          int len;
          while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
          }
          out.closeEntry();
        } finally {
          in.close();
        }
      }
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }
 
  public static List<String> unzip(String zipFilePath, String destDir) throws IOException {
    File dir = new File(destDir);
    if (!dir.exists()) {
      dir.mkdirs();
    }
    byte[] buffer = new byte[1024];
    FileInputStream fis = null;
    ZipInputStream zis = null;
    ZipEntry ze = null;
    List<String> fileList = Lists.newArrayList();
    try {
      fis = new FileInputStream(zipFilePath);
      zis = new ZipInputStream(fis);
      ze = zis.getNextEntry();
      while (ze != null) {
        String fileName = ze.getName();
        File newFile = new File(destDir + File.separator + fileName);
        fileList.add(fileName);
        if (ze.isDirectory()) {
          newFile.mkdirs();
        } else {
          FileOutputStream fos = new FileOutputStream(newFile);
          int len;
          while ((len = zis.read(buffer)) > 0) {
            fos.write(buffer, 0, len);
          }
          fos.close();
        }
        zis.closeEntry();
        ze = zis.getNextEntry();
      }
      zis.closeEntry();
      zis.close();
    } finally {
      if (fis != null) {
        fis.close();
      }
    }
    return fileList;
  }
}
 


반응형
반응형


spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB


반응형

+ Recent posts