일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Java
- Android
- IntelliJ
- es6
- vaadin
- Kotlin
- mybatis
- GIT
- JavaScript
- NPM
- react
- Eclipse
- Python
- window
- SQL
- mapreduce
- plugin
- SSL
- xPlatform
- MSSQL
- hadoop
- R
- Spring
- tomcat
- table
- 공정능력
- SPC
- Sqoop
- 보조정렬
- Express
- Today
- Total
DBILITY
spring boot security 본문
기본 보안 설정(default security configuration)에 대해 알아보고, 기본 설정을 비활성화(disable)하여 사용자 정의 설정으로 변경해 본다.
spring-boot-starter-parent 버전은 2.1.8.RELEASE, spring-cloud 버전은 Greenwich.SR3 이다.
maven project 기준으로 Spring Boot App에 security를 추가하기 위해서는 다음과 같이 pom.xml에 security starter dependancy를 추가한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
spring-boot-autoconfigure-2.1.8.RELEASE.jar에 포함된 SecurityAutoConfiguraton class에 의해 기본 설정 및 초기화가 이루어진다.
기본 설정에 의해 인증(Authenticatoin)은 활성화(enabled) 상태로 content negotiation에 따라 basic 또는 formLogin 방식의 인증이 이루어진다.
spring.security.user.password 속성이 설정되어 있지 않을 시 랜덤 하게 비밀번호가 생성되고, 다음과 같은 형태로 console에 출력된다.
Using generated security password: d493499c-aff8-48ae-897c-72f136116b31
application.properties에 spring.security.user.name 속성, spring.security.user.password 속성을 설정해 주면 된다.
spring security에서 기본적으로 제공하는 사용자는 하나뿐이며, 메모리에 저장한다.
자세한 내용은 Spring Common Application Properties 페이지를 참조한다.
spring.security.user.name=master
#spring.security.user.password=1234
웹브라우저 접속 시 다음과 같이 formLogin이 나타난다. /logout경로 요청 시 logout confirm화면이 나타난다.
자동 설정을 비활성화(disable)하는 방법은 다음과 같이 두 가지다.
첫 번째는 @SpringBootApplication 애노테이션에서 SecurityAutoConfiguration.class를 제외하는 방법으로 다음과 같다.
package com.dbility.spring.cloud.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class SecurityTestApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityTestApplication.class, args);
}
}
다른 방법은 application.properties에서 제외 설정을 하는 것으로 다음과 같다
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
보안을 비활성화할 거면 뭐하러 dependancy를 추가할까?
비활성화하지 않고, 사용자가 원하는 대로 처리할 수 있도록 WebSecurityConfigurerAdapter를 상속받아 configure 메서드를 오버 라이딩하면 된다.
위 두 가지 방법을 사용했던 걸 모두 제거하고, dependancy만 추가한 상태에서 다음과 같이 처리해 보자.
SecurityController.java
package com.dbility.spring.cloud.security;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class SecurityController {
@RequestMapping(value = {"","/"})
public String getDefault() throws Exception {
log.debug("index");
return "<a href='/logout'>logout</a><br />";
}
@GetMapping(path = "/nonsecurity/datetime")
public String getDatetime() throws Exception {
String dt = LocalDateTime.now(ZoneId.of("Asia/Seoul")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")).toString();
log.debug("{}", dt);
return dt;
}
}
ManuallySecurityConfiguration.java
package com.dbility.spring.cloud.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class ManuallySecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("clerk")
.password(passwordEncoder().encode("1234"))
//.password("{noop}1234") // NoOpPasswordEncoder
.roles("USER")
.and()
.withUser("master")
.password(passwordEncoder().encode("1234"))
//.password("{noop}1234") // NoOpPasswordEncoder
.roles("USER","ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/nonsecurity/datetime")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.and()
.formLogin();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
/nonsecurity/datetime 경로는 인증 없이 접근이 가능하고, 그 이외는 모두 인증 화면이 출력된다.
security와 h2db를 연동해 보자. 다음과 같이 depandancy를 추가한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
database 접속 정보를 기술하고, h2 console에 접근할 수 있도록 처리한다.
#mem base
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
#file base
#spring.datasource.url=jdbc:h2:file:/data/testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
/h2-console 웹 접속을 위해서는 /h2-console이하 경로에 대해 접근권한을 주고, csrf는 비활성화한다.
csrf (Cross site request forgery, 사이트 간 요청 위조)는 기본적으로 enable상태로 필요에 따라 적용 방식을 달리할 수 있다.
Clickjacking방지 iframe관련 X-Frame-Options의 경우 동일 도메인 내로 한정한다.
ManuallySecurityConfiguration.java의 configuration은 다음과 같다.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/nonsecurity/datetime","/h2-console/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().ignoringAntMatchers("/h2-console/**")
.and()
.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
.frameOptions().disable()
.and()
.httpBasic()
.and()
.formLogin();
}
/h2-console 경로 접근 시 다음과 같은 인증 화면이 나타난다.
매번 시험용 테이블을 만들고, 데이터를 입력할 수 없으니, 자동으로 생성 및 입력이 가능하도록 src/main/resources아래에 schema.sql과 data.sql을 저장하고, spring app 시작 시 실행되도록 application.properties에 spring.jpa.hibernate.ddl-auto=none으로 설정한다. hibernate가 @Entity 애노테이션이 붙은 클래스를 읽어 자동으로 테이블을 만들어 줄 수 있어 이 기능을 비활성화한 것이다.
application.properties 추가 부분
spring.jpa.hibernate.ddl-auto=none
spring.datasource.schema=classpath*:schema.sql
spring.datasource.data=classpath*:data.sql
schema.sql
CREATE TABLE IF NOT EXISTS Member (
username VARCHAR(20) NOT NULL,
password VARCHAR(20) NOT NULL,
role VARCHAR(20) NOT NULL,
PRIMARY KEY(username)
);
data.sql
INSERT INTO Member VALUES ('master','1234','ADMIN');
INSERT INTO Member VALUES ('clerk','1234','USER');
Member.java
package com.dbility.spring.cloud.security;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Member {
@Id
private String username;
private String password;
private String role;
}
MemberRepository.java ( Persistent layer )
package com.dbility.spring.cloud.security;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
Member findByUsername(String username);
}
인증을 위해 사용자 정보를 가져올 수 있게 UserDetailService 인터페이스를 구현한다.
package com.dbility.spring.cloud.security;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service("userDetailService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private MemberRepository repository;
@Resource(name = "passwordEncoder")
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Member member = repository.findByUsername(username);
if(member == null) {
throw new UsernameNotFoundException(username);
}
UserDetails details = User.builder().username(member.getUsername())
.password(passwordEncoder.encode(member.getPassword()))
.roles(member.getRole())
.build();
return details;
}
}
ManuallySecurityConfiguration.java 일부
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/*
auth
.inMemoryAuthentication()
.withUser("clerk")
.password(passwordEncoder().encode("1234"))
//.password("{noop}1234") // NoOpPasswordEncoder
.roles("USER")
.and()
.withUser("master")
.password(passwordEncoder().encode("1234"))
//.password("{noop}1234") // NoOpPasswordEncoder
.roles("USER","ADMIN");
*/
auth.userDetailsService(userDetailsService);
}
'java > spring cloud' 카테고리의 다른 글
spring boot security mybatis (0) | 2019.09.23 |
---|---|
spring boot security jdbcdatasource (0) | 2019.09.21 |
Authentication is required but no CredentialsProvider has been registered (0) | 2019.09.18 |
spring boot actuator + prometheus + grafana visualization (0) | 2019.09.16 |
spring boot actuator + embedded mongodb (0) | 2019.09.04 |