본문으로 바로가기

[스프링] 11. 스프링 시큐리티

category SPRING/스프링 2021. 4. 26. 21:36
728x90
반응형
SMALL

스프링 웹 시큐리티 구조

기본 동작 방식은 서블릿의 여러 종류의 필터인터셉터를 이용해서 처리된다.

필터와 인터셉터는 특정한 서블릿이나 컨트롤러의 접근에 관여한다는 점에서는 유사하나

필터스프링과 무관하게 서블릿 자원이고, 인터셉터스프링의 빈으로 관리되어 스프링의 컨텍스트 내에 속한다.

 

스프링 시큐리티를 이용하게 되면 위의 그림과 같이 인터셉터와 필터를 이용하면서 별도의 컨텍스트를 생성해서 처리된다.

의존성 4가지 주입

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>5.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>5.4.6</version>
</dependency>
		

CSRF(Cross-site request forgery) 공격과 토큰


 스프링 시큐리티에서 POST 방식을 이용하는 경우 기본적으로 CSRF 토큰이라는 것을 이용

별도의 설정이 없다면 스프링 시큐리티가 적용된 모든 사이트의 POST 방식에는 CSRF 토큰이 사용되는데

"사이트간 위조 방지" 를 목적으로 특정한 값의 토큰을 사용하는 방식이다.

 

CSRF 공격은 사용자의 요청에 대한 출처를 검사하지 않아서 생기는 허점이기 때문에 

사용자의 요청에 대한 출처를 의미하는 referer 헤더를 체크하거나 일반적인 경우 잘 사용되지 않고 REST 방식에서 사용되는 PUT, DELETE 와 같은 방식을 이용하는 등의 방식을 고려한다.

 

서버에서 생성하는 토큰은 일반적으로 난수를 생성하여 공격자가 패턴을 찾을 수 없도록 한다.

<input type="hidden" name="${_csrf.parameterName }" value="${_csrf.token }" />

 

일반적으로 CSRF 토큰은 세션을 통해서 보관하고, 브라우저에서 전송된 CSRF 토큰값을 검사하는 방식으로 처리한다.

스프링 시큐리티 에서는 CSRF 토큰 생성을 비활성화 하거나 CSRF 토큰을 쿠기를 이용해서 처리하는 등의 설정을 지원한다.

<security:csrf disable="true"/>

 


#DB가 있는 상태에서 Mybatis 같은 프레임워크 없이  간편 인증/권한 처리하기

 

security-context.xml 추가 , root-context.xml 에 dataSource 에 관한 bean등록
<!-- DB가 있는 상태에서 Mybatis 같은 프레임워크 없이 사 간편 인증/권한 처리하기 -->
			<security:jdbc-user-service data-source-ref="dataSource" />

 

sql 생성
create table users(
      username varchar2(50) not null primary key,
      password varchar2(50) not null,
      enabled char(1) default '1');

      
 create table authorities (
      username varchar2(50) not null,
      authority varchar2(50) not null,
      constraint fk_authorities_users foreign key(username) references users(username));
      
 create unique index ix_auth_username on authorities (username,authority);


insert into users (username, password) values ('user00','pw00');
insert into users (username, password) values ('member00','pw00');
insert into users (username, password) values ('admin00','pw00');

insert into authorities (username, authority) values ('user00','ROLE_USER');
insert into authorities (username, authority) values ('member00','ROLE_MANAGER'); 
insert into authorities (username, authority) values ('admin00','ROLE_MANAGER'); 
insert into authorities (username, authority) values ('admin00','ROLE_ADMIN');
commit;


select * from users;

select * from authorities order by authority;

create table tbl_member(
      userid varchar2(50) not null primary key,
      userpw varchar2(100) not null,
      username varchar2(100) not null,
      regdate date default sysdate, 
      updatedate date default sysdate,
      enabled char(1) default '1');


create table tbl_member_auth (
     userid varchar2(50) not null,
     auth varchar2(50) not null,
     constraint fk_member_auth foreign key(userid) references tbl_member(userid)
);

commit

select * from users;

 

#스프링 시큐리티 5부터는 PasswordEncoder를 지정해야함

PasswordEncoder 구현
import org.springframework.security.crypto.password.PasswordEncoder;

//비밀번호를 안전하게 저장할 수 있도록 비밀번호의 단방향 암호화를 지원하는 PasswordEncoder 인터페이스와 구현체들을 제공합니다
public class CustomNoOpasswordEncoder implements PasswordEncoder {

	//비밀번호를 단방향 암호화
	@Override
	public String encode(CharSequence rawPassword) {
		
		log.warn("Before encode: " + rawPassword);
		return rawPassword.toString();
	}

	//암호화되지 않은 비밀번호(raw-)와 암호화된 비밀번호(encoded-)가 일치하는지 비교
	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		
		log.warn("mathes: "+ rawPassword + ":" + encodedPassword);
		return rawPassword.toString().equals(encodedPassword);
	}

}

 

#BCryptPassWordEncoder 클래스를 이용한 패스워드 암호화 처리

bcrypt는 패스워드를 저장하는 용도로 설계된 해시 함수로 특정 문자열을 암호화하고 

체크하는 쪽에서는 암호화된 패스워드가 가능한 패스워드인지만 확인하고 다시 원문으로는 돌리지 못함

security-context.xml 추가
<bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="bcryptPasswordEncoder"/>



<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				
			</security:user-service>
			
            
			<security:password-encoder ref="bcryptPasswordEncoder"/>
            
		</security:authentication-provider>
	</security:authentication-manager>

 

TEST 코드
public class MemberTests {
	
	@Setter(onMethod_ = @Autowired)
	private PasswordEncoder pwencoder;
	
	@Setter(onMethod_ = @Autowired)
	private DataSource ds;
	
	
	//회원정보 넣을때 비번 암호화 되는지 테스트
	@Test
	public void testInsertMember() {
		
		String sql = "insert into tbl_member(userid, userpw, username) values (?,?,?)";
		
		for(int i=0; i< 100; i++) {
			
			Connection con = null;
			PreparedStatement pstmt = null;
			
			try {
				con = ds.getConnection();
				pstmt = con.prepareStatement(sql);
				
				pstmt.setString(2, pwencoder.encode("pw" + i));
				
				if(i<80) {
					pstmt.setString(1, "user" +i);
					pstmt.setString(3, "일반사용자" +i);
				}else if(i<90) {
					pstmt.setString(1, "manager" +i);
					pstmt.setString(3, "운영자" +i);
				}else {
					pstmt.setString(1, "admin" +i);
					pstmt.setString(3, "관리자" +i);
				}
				
				pstmt.executeUpdate();
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				if(pstmt !=null) {
					try {
						pstmt.close();
					} catch (Exception e) {} }
				
				if(con !=null){
					try {
						con.close();
					} catch (Exception e) {} }
			}
		}
	}
    }

 

비밀번호가 암호화되어 DB 에 갱신되있음을 확인

728x90
반응형
LIST