Category: Spring

0

Spring Security 권한 계층 사용하기 - @RoleHierarcy

목차 Spring Security 권한 계층 사용하기 - @RoleHierarcy Spring Security - DelegateFilterProxy Spring Security - Remember Me와 관련된 인증 API - RememberMeConfigurer Spring Security - RembmerMeAuthenticationFilter Spring Security - SessionManagementFilter & ConcurrentSessionFilter Spring Security - 인가 API ExpressionUrlAuthorizationConfigurer Spring Security - Security 설정을 위한 WebSecurityConfigurerAdatper Spring Security - AuthenticationProvider Spring Security - AuthenticationManager Spring Security - UsernamePasswordAuthenticationFilter & AbstractAuthenticationProcessingFilter Spring Security - SecurityContextHolder 와 SecurityContext Spring Security - Authentication 객체 @RoleHierarcy우리는 일반적으로 ADMIN, MANAGER, USER 권한이 있다고 하면 Admin 권한으로 모든 자원에 접근이 가능하다고 생각하지만 Spring Security는 상하위 개념으로 인식하지 못하고 권한들을 독립적으로 인식해 MANAGER 권한, USER 권한 리소스에 접근할 수 없다. 만약 접근하려고 하면 접근 권한과 관련된 403(Forbidden) Error 가 발생한다. 권한간 상하 관계를 적용하기 위해 Spring Security에서는 RoleHierarchy 인터페이스를 제공하고 구현체로는 RoleHierarchyImpl 를 제공한다.권한을 적용하기 위해 RoleHierarchyVoter 객체에 RoleHierarchy 객체를 넣어줌으로써 권한 계층 구조를 사용할 수 있다. RoleHierarchy : 권한간 계층 관계 정보를 저장 RoleHierarchyVoter 인가 처리시 RoleHierarchy 내 저장된 권한간 계층 정보를 사용할 수 있도록 한다. 권한을 저장하기 위한 Entity@Entity@Table(name = "ROLE_HIERARCHY")@AllArgsConstructor@NoArgsConstructor@Data@Builder@ToString(exclude = {"parentName", "roleHierarchy"})public class RoleHierarchy implements Serializable { @Id @GeneratedValue private Long id; @Column(name = "child_name") private String childName; @ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY) @JoinColumn(name = "parent_name", referencedColumnName = "child_name") private RoleHierarchy parentName; @OneToMany(mappedBy = "parentName", cascade = {CascadeType.ALL }) private Set<RoleHierarchy> roleHierarchy = new HashSet<>();}

0

Spring Security - Security 설정을 위한 WebSecurityConfigurerAdatper

목차 Spring Security 권한 계층 사용하기 - @RoleHierarcy Spring Security - DelegateFilterProxy Spring Security - Remember Me와 관련된 인증 API - RememberMeConfigurer Spring Security - RembmerMeAuthenticationFilter Spring Security - SessionManagementFilter & ConcurrentSessionFilter Spring Security - 인가 API ExpressionUrlAuthorizationConfigurer Spring Security - Security 설정을 위한 WebSecurityConfigurerAdatper Spring Security - AuthenticationProvider Spring Security - AuthenticationManager Spring Security - UsernamePasswordAuthenticationFilter & AbstractAuthenticationProcessingFilter Spring Security - SecurityContextHolder 와 SecurityContext Spring Security - Authentication 객체 Spring Security 설정하기 - WebSecurityConfigurerAdatper스프링 Scurity에 대한 설정은 WebSecurityConfigurer 인터페이스 구현하거나 WebSecurityConfigurerAdapter 클래스를 상속해 설정을 할 수 있다. 인증 방식 설정 configure(AuthenticationManagerBuilder auth) 인증 방식과 관련된 설정을 하기 위한 메소드 AuthenticationManagerBuilder는 인증객체(AuthenticationManager)를 생성하기 위한 클래스이다. In Memory authentication나 UserDetailsService를 사용하는 다양한 인증 방식을 손 쉽게 구축할 수 있도록 지원한다. configure(AuthenticationManagerBuilder auth) private final CustomUserDetailsService customUserDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserDetailsService);}

0

Spring-JWT(JSON Web Token) - 4. JWT 다루기

4. JWT 다루기목차 Spring-JWT(JSON Web Token) - 4. JWT 다루기 Spring-JWT(JSON Web Token) - 3. Spring Security 적용하기 Spring-JWT(JSON Web Token) - 2. 회원가입 Spring-JWT(JSON Web Token) - 1. JWT 알아보기 JWT를 다루기 위한 Util Class만들기 JwtUtil 객체는 Jwts.builder를 이용해 JWT를 생성하고 Jwts.parserBuilder를 이용해 Token을 JWT로 변환시켜 데이터를 가져오도록 한다. Jwts.builder(JwtBuilder) setHeader JWT의 Header 에 대한 설정을 위한 메소드 setSubject, setExpiration, setIssuer, setAudience, setNotBefore, setIssuedAt Registed Payload는 각각에 해당하는 set 메소드들이 제공된다. setClaims JWT의 Claim 데이터 를 추가하기 위한 메소드 signWith Header와 Payload를 갖고 Secret Key 로 서명한다. 암호화 알고리즘으로는 보통 HMAC or RSA 알고리즘을 사용한다. Jwts.parserBuilder(JwtParser) setSigningKey 데이터 위변조 확인을 위해 secretKey를 이용해 JWS에 대한 유효성 검증을 한다. secretKey를 이용한 검증 실패시 해당 JWT는 사용하지 못한다. parseClaimsJws token을 JWS로 파싱해준다.

0

Spring-JWT(JSON Web Token) - 3. Spring Security 적용하기

3. 로그인목차 Spring-JWT(JSON Web Token) - 4. JWT 다루기 Spring-JWT(JSON Web Token) - 3. Spring Security 적용하기 Spring-JWT(JSON Web Token) - 2. 회원가입 Spring-JWT(JSON Web Token) - 1. JWT 알아보기 의존성 추가Spring Boot에 Security를 적용하기 위해 Spring Security 의존성과 JWT를 사용하기위해 jjwt 의존성을 추가해주도록 한다. build.gradle dependencies { .... implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' ....} Security 설정하기Spring Security는 인증에 필요한 사용자 정보를 저장할 때 비밀번호는 PasswordEncoder 객체를 이용해 암호화된 Password로 저장돼야 한다. 현재 프로젝트에서는 PasswordEncoder를 구현한 BcryptPasswordEncoder 객체를 이용해 암호화를 할 것이다.

0

Spring-JWT(JSON Web Token) - 2. 회원가입

2. 회원가입목차 Spring-JWT(JSON Web Token) - 4. JWT 다루기 Spring-JWT(JSON Web Token) - 3. Spring Security 적용하기 Spring-JWT(JSON Web Token) - 2. 회원가입 Spring-JWT(JSON Web Token) - 1. JWT 알아보기 의존성 관계build.gradle dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test'} 회원가입을 위한 DTO 만들기SignUpRequest.java @Data@AllArgsConstructor@NoArgsConstructorpublic class SignUpRequest { private String username; private String password;}

0

Spring Boot 게시판 만들기 23 - Log4jdbc를 이용해 Query 로그 기록하기

23. Log4jdbc를 이용해 Query 로그 기록하기로그 레벨 TRACE > DEBUG > INFO > WARN > ERROR 로그 레벨은 TRACE로 갈 수록 표현되는 Log의 양이 많아지고 ERROR로 갈 수록 표현되는 Log의 양이 적어진다. Configure 파일 문법Logback Configure파일을 구성하는 3가지 태그가 있다. appender 로그의 출력 위치, 출력 형식및 로깅에 대한 설정(ex. 기간, 용량 등)을 지정한다 root 전역적인 로그 설정을 위해 사용한다. appender를 사용해 appender에 정의된 형식과 위치로 로그를 출력 여러개의 appender를 사용할 수 있다. logger root와는 반대로 class나 Package단위로 Logging 정책을 설정할 수 있다. 로그 메시지를 작성하기 위해 사용하는 클래스를 명시하고 appender를 사용해 appender에 정의된 형식과 위치로 로그를 출력한다. 여러개의 appender를 사용할 수 있다. appender 클래스의 종류

0

Spring Boot 게시판 만들기 22 - Github Action 사용하기

22. Github Action 사용하기이전에 Jenkins를 이용해 CI 환경을 구성했다. Jenkins를 이용한 CI 환경의 가장 큰 단점은 Github로부터 Web Hook을 받을 수 있게 Port를 열어둬야 한다는 것이다. ngrok을 이용하더라도 계속해서 Port를 열어둘 수는 없는 노릇이라서 Github에서 제공하는 CI 도구인 Github Action을 이용하기로 했다. Github Action Workflow Template 선택하기Github에서 프로젝트를 선택하면 상단에 Action을 선택하면 본인의 환경에 맞는 Build 환경을 위한 Template를 선택할 수 있다. Gradle을 이용해 Spring Boot 프로젝트를 진행하고 있으므로 Gradle을 선택하도록 한다. Template 수정하기Gradle을 선택하게 되면 기본적으로 Gradle을 이용한 Build Plan Template가 주어진다. 기본적인 Template에서는 JDK 버전이 1.8인데 프로젝트는 JDK 11을 이용해 진행하고 있으므로 변경해줄 필요가 있다. 그 외에 운영체제나 원하는 Build Branch들을 선택할 수 있다.

0

스프링 MVC 시작하기 - XML로 세팅하기

스프링 MVC 시작하기 - XML로 세팅하기 Web.xml 설정하기 root-context.xml 설정하기 servlet-context 설정하기 의존관계 설정하기<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springMVCstep3</artifactId> <version>1.0-SNAPSHOT</version> <properties> <javax.servlet-version>4.0.1</javax.servlet-version> <javax.servlet.jsp-version>2.3.3</javax.servlet.jsp-version> <javax.servlet.jsp.jstl-version>1.2</javax.servlet.jsp.jstl-version> <org.springframework-version>5.2.2.RELEASE</org.springframework-version> <!-- <org.springframework-version>4.3.25.RELEASE</org.springframework-version> --> </properties> <!-- 라이브러리 셋팅 --> <dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet-version}</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>${javax.servlet.jsp-version}</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>${javax.servlet.jsp.jstl-version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> </dependencies> <build> <finalName>springMVCstep3</finalName> <sourceDirectory>src/main/java</sourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <pluginManagement> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.1</version> <configuration> <warSourceDirectory>WebContent</warSourceDirectory> </configuration> </plugin> </plugins> </pluginManagement> </build> <!-- 라이브러리 버전관리 --></project> Web.xml 설정하기 톰캣 DispatcherServlet 을 스프링 DispatcherServlet 으로 바꾸기 Bean을 정의할 xml 파일을 정의하기 Listener 를 설정한다. 파라미터 인코더 를 설정한다. <!-- 현재 웹 애플리케이션에서 받아들이는 모든 요청에 대해 appServlet이라는 이름으로 정의되어 있는 서블릿을 사용하겠다. --><servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern></servlet-mapping><!-- 요청 정보를 분석해서 컨트롤러를 선택하는 서블릿을 지정한다. --><servlet> <servlet-name>appServlet</servlet-name> <!-- Spring MVC에서 제공하고 있는 기본 서블릿을 지정한다. --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- Spring MVC 설정을 위한 xml 파일을 지정한다. --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup></servlet> <!-- Bean을 정의할 xml 파일을 지정한다. --><context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/root-context.xml</param-value></context-param> <!-- 리스너설정 --><listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

0

Spring Boot 게시판 만들기 20 - 프록시 서버 이용하기

20. 프록시 서버 이용하기현재는 client 가 Web Application에 접근하기 위해서는 8080포트를 사용해야 한다. client가 쉽게 접근 할 수 있도록 80번 포트로 어플리케이션을 띄어야 하는데 ubuntu에서는 일반 사용자가 80번 포트를 사용할 수 있는 권한이 없다. 만약 80번 포트로 프로그램을 실행하게 되면 아래와 같은 권한 오류가 뜨게 된다. 그렇다고 어플리케이션을 띄울 때 마다 매번 sudo권한으로 실행시킬 수도 없는 노릇이다. 맥에서는 80번 포트로 잘 열리는데 우분투에서 오류가 떠서 당황 했다. 우분투에서 1024미만 포트는 일반 사용자가 사용할 수 없는 포트이다. Caused by: java.net.SocketException: Permission denied at java.base/sun.nio.ch.Net.bind0(Native Method) ~[na:na] at java.base/sun.nio.ch.Net.bind(Net.java:455) ~[na:na] at java.base/sun.nio.ch.Net.bind(Net.java:447) ~[na:na] at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:227) ~[na:na] at java.base/sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:80) ~[na:na] at org.apache.tomcat.util.net.NioEndpoint.initServerSocket(NioEndpoint.java:228) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41] at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:211) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41] at org.apache.tomcat.util.net.AbstractEndpoint.bindWithCleanup(AbstractEndpoint.java:1159) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41] at org.apache.tomcat.util.net.AbstractEndpoint.start(AbstractEndpoint.java:1245) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41] at org.apache.coyote.AbstractProtocol.start(AbstractProtocol.java:603) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41] at org.apache.catalina.connector.Connector.startInternal(Connector.java:1064) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41] ... 29 common frames omitted nginx를 이용해 프록시 서버 만들어주기80번 포트를 사용하기 위해 Proxy 서버를 띄어 놓고 Proxy 서버로 요청이 들어오면 Spring Boot 프로그램으로 Forwarding 해주도록 설정을 할 것이다. sudo 명령어를 사용해 프로그램을 시키는 방법도 있지만, 매번하기에는 번거롭다는 단점도 있고 /etc/sudoers에 패스워드 권한을 풀어줄 수도 있지만 보안상의 문제가 있어서 가장 안전한 방법으로 Proxy 서버를 이용하기로 했다. nginx 설치하기

0

Spring Boot 게시판 만들기 19 - Build시 DB 연결 Bug Fix 및 Profile 설정

19. Build시 DB 연결 Bug Fix 및 Profile 설정Jenkins에서 build 오류Jenkins에서 Build를 진행하면 Test시 해당 오류가 발생하게 된다. Test를 진행하면서 DBConfigByYml 클래스내 필드를 Application-mysql.yml 를 이용해 채우게 되는데 Application-mysql.yml 파일이 Github에는 올라가 있지 않아 문제가 생기게 되는 것이다. > Task :testSampleBoardApplicationTests > contextLoads() FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132 Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800 Caused by: org.springframework.beans.factory.BeanCreationException at AutowiredAnnotationBeanPostProcessor.java:405 Caused by: java.lang.IllegalArgumentException at PropertyPlaceholderHelper.java:1782021-02-24 16:17:02.458 INFO 1610 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'17 tests completed, 1 failed> Task :test FAILEDFAILURE: Build failed with an exception.* What went wrong:Execution failed for task ':test'.> There were failing tests. See the report at: file:///var/jenkins_home/workspace/sample-board/build/reports/tests/test/index.html* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 53s7 actionable tasks: 7 executedBuild step 'Invoke Gradle script' changed build result to FAILUREBuild step 'Invoke Gradle script' marked build as failureFinished: FAILURE 여러개의 @PropertySource 사용하기첫번째로 해결한 방법은 @PropertySource를 두개 사용하여 test를 진행할 시에는 H2 database를 바라보게 하고 운영시에는 MySQL을 바라 볼 수 있게 설정 파일을 두게 만들어 사용했다. @PropertySource는 순서에 따라 아래 값이 이전 값을 덮어 쓰게 된다. DBConfigByYml.java @Configuration@PropertySource(value = "classpath:application-h2.yml", factory = YamlPropertySourceFactory.class, ignoreResourceNotFound = true)@PropertySource(value = "classpath:application-mysql.yml", factory = YamlPropertySourceFactory.class, ignoreResourceNotFound = true)public class DBConfigByYml { @Value("${spring.datasource.driver-class-name}") private String driverClassName; @Value("${spring.datasource.url}") private String url; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Bean public DataSource dataSource() { return DataSourceBuilder.create() .driverClassName(driverClassName) .url(url) .username(username) .password(password) .build(); }} 여러개의 PropertySource 묶기

0

Spring Boot 게시판 만들기 18 - 외부로부터 설정 받기(yml)

18. 외부로부터 설정 받기(yml)YamlPropertySourceFactory.java public class YamlPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { Properties propertiesFromYaml = loadYamlIntoProperties(resource); String sourceName = name != null ? name : resource.getResource().getFilename(); return new PropertiesPropertySource(sourceName, propertiesFromYaml); } private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException { try { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(resource.getResource()); factory.afterPropertiesSet(); return factory.getObject(); } catch (IllegalStateException e) { // for ignoreResourceNotFound Throwable cause = e.getCause(); if (cause instanceof FileNotFoundException) { throw (FileNotFoundException) e.getCause(); } throw e; } }} DBConfigByYml.java @Configuration@PropertySource(value = "classpath:application-mysql.yml", factory = YamlPropertySourceFactory.class)public class DBConfigByYml { @Value("${spring.datasource.driver-class-name}") private String driverClassName; @Value("${spring.datasource.url}") private String url; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Bean public DataSource dataSource(){ return DataSourceBuilder.create() .driverClassName(driverClassName) .url(url) .username(username) .password(password) .build(); }}

0

Spring Boot 게시판 만들기 17 - 외부로부터 설정 받기(property)

17. 외부로부터 설정 받기(property)Github에 프로젝트를 올릴때 데이터 베이스 접속 연결과 같은 민감한 정보는 다른 사람이 열람해서는 안되는 정보라 형상관리에서 제외한 상태로 올리게 된다. .gitignore **/application-mysql.yml**/application-mysql.properties 데이터베이스 접속을 위한 properties 만들기application-mysql.properties spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/{테이블}serverTimezone=UTC&characterEncoding=UTF-8spring.datasource.username={Username}spring.datasource.password={Password} TransactionManagementConfigurer은 Spring에서 데티어베이스 연결을 지원한다. DBConfig.java

0

스프링 핵심 원리 이해 - 객체 지향 원리 적용

목차 Post not found: spring-boot/spring-core/basic/basic2 객체 지향 설계 5원칙 SOLID DIP 원칙 위반 - 추상화와 구체화 둘다 의존MemberServiceImpl 클래스는 MemberRepository 인터페이스를 의존하면서 구현 클래스인 MemoryMemberRepository도 의존하고 있다. public class MemberServiceImpl implements MemberService{ // MemberServiceImpl 클래스는 MemberRepository 인터페이스와 구현체인 MemoryMemberRepository 둘다 의존하고 있다. private final MemberRepository memberRepository = new MemoryMemberRepository(); @Override public void join(Member member) { memberRepository.save(member); } @Override public Member findMember(Long memberId) { return memberRepository.findById(memberId); }} OrderServiceImpl 클래스는 MemberRepository 인터페이스와 DiscountPolicy 인터페이스를 의존하면서 구현 클래스인 MemoryMemberRepository 와 RateDiscountPolicy 를 의존하고 있다. 추상화와 구체화 둘다 의존하는 문제가 있다.–> DIP 위반 DIP의 원칙을 지키기 위해 의존 class내에서 구현 클래스를 가져오는 부분을 삭제하게 되면 객체가 존재하지 않아서 nullPointException이 발생한다. public class OrderServiceImpl implements OrderService { // OrderServiceImpl 클래스는 MemberRepository 인터페이스와 구현체인 MemoryMemberRepository // DiscountPolicy 인터페이스와 구현체인 RateDiscountPolicy 를 의존하는 문제점이 있다. private final MemberRepository memberRepository = new MemoryMemberRepository(); private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); @Override public Order createOrder(Long memberId, String itemName, int itemPrice) { Member member = memberRepository.findById(memberId); int discountPrice = discountPolicy.discount(member, itemPrice); return new Order(memberId, itemName, itemPrice, discountPrice); }}