DBILITY

spring boot security jdbcdatasource 본문

java/spring cloud

spring boot security jdbcdatasource

DBILITY 2019. 9. 21. 14:25
반응형

이전에는 inMemory와 사용자 정의 인증을 살펴보았다.

이번에는 jdbcAuthentication을 사용해 보자.

 

dependancy를 추가한다.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>

application.properties에 h2 database설정 및 관련 테이블과 데이터 생성을 추가한다.

#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.datasource.initialization-mode=always

spring.datasource.schema=classpath*:schema.sql
spring.datasource.data=classpath*:data.sql

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

schema.sql

CREATE TABLE IF NOT EXISTS Member (
	username VARCHAR(20) NOT NULL,
	password VARCHAR(255) NOT NULL,
	enabled BOOLEAN NOT NULL,
	PRIMARY KEY(username)
);

CREATE TABLE IF NOT EXISTS Role (
	username VARCHAR(20) NOT NULL,
	authority VARCHAR(20) NOT NULL,
	FOREIGN KEY(username) REFERENCES Member(username)
);

data.sql

INSERT INTO Member VALUES ('master','{bcrypt}$2a$10$MPuZy36Gzf9awY7czwUWAOAJmDZ1EA6tIsJEf6p/XHuzt8R532pSm',1);
INSERT INTO Member VALUES ('clerk','{noop}1234',1);

INSERT INTO Role VALUES ('master','ADMIN');
INSERT INTO Role VALUES ('master','USER');
INSERT INTO Role VALUES ('clerk','USER');

spring 5.0에선 password encoding시 PasswordEncoderFactories.createDelegatingPasswordEncoder를 사용하게 되는 게 구현 소스를 참고하자.

default는 BCryptPasswordEncoder를 사용한다.{noop}는 plaintext로 저장된 것이다.

 

ManuallySecurityConfiguration.java

package com.dbility.spring.cloud.security.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;

@Configuration
@EnableWebSecurity
public class ManuallySecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Autowired
	private DataSource dataSource;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {

		auth.jdbcAuthentication()
		.passwordEncoder(passwordEncoder())
		.dataSource(dataSource)
		.usersByUsernameQuery("select username,password,enabled from Member where username=?")
		.authoritiesByUsernameQuery("select username,authority from Role where username=?")
		.rolePrefix("ROLE_");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		http
		.authorizeRequests()
		.antMatchers("/nosecurity/datetime")
		.permitAll()
		.antMatchers("/h2-console/**").hasRole("ADMIN")
		.anyRequest()
		.authenticated()
		.and()
		.csrf().ignoringAntMatchers("/h2-console/**")
		.and()
		.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
		.frameOptions().disable()
		.and()
		.httpBasic()
		.and()
		.formLogin();
	}

	@Bean(name = "passwordEncoder")
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}

ManuallyErrorController.java

package com.dbility.spring.cloud.security.controller;

import java.util.HashMap;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class ManuallyErrorController implements ErrorController {

	private static final String PATH = "/error";

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

	@RequestMapping(value = PATH)
	public HashMap<String, Object> handleError(HttpServletRequest request) {

		HashMap<String, Object> retMap = new HashMap<String, Object>();

		Object statusCode = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
		HttpStatus status = HttpStatus.valueOf(Integer.valueOf(statusCode.toString()));
		Exception exception = (Exception)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

		retMap.put("StatusCode", statusCode);
		retMap.put("StatusMessage", status);
		retMap.put("ErrorMessage", exception != null ? exception.getMessage(): "NA");
		if(log.isDebugEnabled()) {
			log.debug("Error --> {}", retMap);
		}
		return retMap;
	}

}

 

권한이 없는 경로에 접근시 다음과 같이 json data가 반환된다.

{"ErrorMessage":"NA","StatusCode":403,"StatusMessage":"FORBIDDEN"}

반응형
Comments