-
스프링 시큐리티(Spring Security)를 이용한 보안 강화보안(Security)/Spring Security 2018. 9. 10. 10:18
보안에 있어 가장 기본적이고 중요한 개념이 인증(Authentication)과 권한부여(Authorization) 두가지다.
- 인증(Authentication)은 어플리케이션의 사용자가 사용자가 주장하는 본인이 맞는지 확인하는 절차로 3가지 인증기법이 주로 사용된다.
* 크리덴셜 기반 인증 : 사용자명과 비밀번호를 이용한 방식
* 이중 인증 : ATM 기기를 이용할 때처럼 물리적인 카드와 사용자가 입력한 개인정보 조합하는 방식
* 하드웨어 인증 : 자동차 키를 통해 운전할 수 있는지 없는지를 가늠
권한부여(Authorization)란 인증을 통해 인증된 주체를 하나 이상의 권한을 부여해 보호되는 자원들에 대한 접근 가능여부를 할당하는 것을 말한다.
- 인증이 되었더라도 권한이 없다면 사용할 수 없는 게시판이 존재함
Spring Framework 기반의 어플리케이션의 인증과 권한부여를 좀더 쉽게 할 수 있도록 Spring Security를 제공한다.
- filter 기반으로 동작해 Spring MVC의 구현과 완전히 분리시켜 관리 가능하다.
- 권한 기반의 허가를 지원하므로 경로별, 권한별 리소스 제한에 대해서도 많은 기능 적용 가능하다(지금은 잘 이해가 가지 않는다)
이를 위해서 4개의 라이브러리가 필요하다.
spring-security-web
aop-alliance
spring-sercurity-core
spring-security-confighttp://mvnrepository.com/
Maven Repository 접속해 다운받는다.
※ 이클립스나 STS에 맞는 버전이 있으므로 호환성 꼭 확인하고 다운받도록 한다.
spring-security 파일들은 4.0.0
aop-alliance는 1.0 선택했다.다운받은 파일들을 Root Context 하위에 붙여넣는다 (폴더 만들어 관리 추천)
- web.xml에 세션 리스너, 보안 필터 추가한다.<listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy </filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
HttpSessionEventPublisher : 한 사용자가 다른 브라우저로 동시 로그인 막는 리스너
DelegatingFilterProxy : 웹으로 들어오는 요청에 대해 스프링 시큐리티가 관여하도록 만들어주는 프록시 필터. 이를 통해 인증 및 권한 체크한다.<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <sec:http auto-config="true"> <sec:intercept-url pattern="/resources/**" access="permitAll"/> <sec:intercept-url pattern="/" access="permitAll"/> <sec:intercept-url pattern="/memberlogin" access="permitAll"/> <sec:intercept-url pattern="/member/login" access="permitAll"/> <sec:intercept-url pattern="/member/registry" access="permitAll"/> <sec:intercept-url pattern="/member" access="hasRole('ROLE_USER')"/> <sec:intercept-url pattern="/member/**" access="hasRole('ROLE_USER')"/> <sec:csrf disabled="true" /> <sec:form-login login-page="/" username-parameter="id" password-parameter="password" login-processing-url="/memberlogin" default-target-url="/member/login" authentication-failure-url="/" always-use-default-target="true" /> <sec:logout logout-success-url="/" logout-url="/member/logout2" invalidate-session="true" /> <sec:session-management invalid-session-url="/"> <sec:concurrency-control max-sessions="1" expired-url="/" /> </sec:session-management> </sec:http> <bean id="userService" class="kr.co.hucloud.security.UserService"> <property name="memberDAO" ref="memberDAO" /> </bean> <sec:authentication-manager> <sec:authentication-provider ref="userService" /> </sec:authentication-manager> </beans>
securityContext.xml 작성해 Context Root 하위에 위치시킨다.
스프링 시큐리티를 사용하기 위한 설정들을 정의한다.
* <sec:http auto-config="true" > : http 경로 설정
* <sec:intercept-url pattern="url" access="ROLE"> 웹 사이트 경로에 대한 접근 권한 제어할 수 있도록 한다.
url 에 대해 "ROLE"이라는 권한이 있어야 접근할 수 있다.
* hasIpAddress(ipAddress) : 특정 IP 주소 또는 넷마스크 포함하는 IP 주소와 일치성 비교한다.
access="hasIpAddress('192.168.201.24')" / access="hasIpAddress('192.168.201.24/224')"
*hasRole(role) Role 과 GrantedAuthority와의 일치성 비교한다.
hasAnyRole(role) GrantedAuthority에 일치하는 권한이 있는지 비교한다.
access에 넣을 수 있는 값들. 모두 "" 안에 입력해야 한다
- permitAll : 모든 사용자 접근 허가
- denyAll : 모든 사용자 접근 거부
- anonymous : 익명 사용자 접근 허가
- authenticated : 인증된 사용자에게만 허가
- rememberMe : remember Me 기능으로 인증된 사용자에게만 접근 허가 (이해 안감)
- fullyAuthenticated : 완전한 크리덴셜로 인증된 사용자에게만 접근 허가
* <sec:csrf disabled="true"> : csrf 보안을 설정한다.
- Spring Security 4부터 추가되었고, true 설정시 모든 페이지에 token 값이 존재해야 한다.
* <sec:form-login... />
스프링 시큐리티 사용해 로그인할 설정
- login-page : login 폼(form)이 존재하는 페이지 url
- username-parameter : login 폼에서 id 태그의 name 속성값
- password-parameter : login 폼에서 비밀번호 태그의 name 속성값
- login-processing-url : 스프링 시큐리티를 이용해 로그인 처리할 url
- AuthenticationProviter 구현부
- default-target-url : 로그인 정상적으로 완료되면 이동할 페이지 url
- authentication-failure-url : 로그인 실패시 이동할 url
- always-use-default-target
true 지정하면 로그인 성공시 default-target-url로 무조건 이동
false로 지정하면 다른 페이지로 이동할 수 있도록 한다(이때 default-target-url 아닌 authentication-success-handler 이용해야 한다. 이해 안감)
* <sec:logout> 로그아웃 처리할 url 설정
logout-success-url : 로그아웃 성공시 이동할 페이지
logout-url : 로그아웃 처리할 url 스프링 시큐리티가 만들어주기 때문에 Context 파일에 입력만 해두고 jsp에서 요청하면 된다.
invalidate-session : 로그아웃시 세션 삭제여부 "true" 또는 "false"로 입력
<sec:session-management> : 세션 관련 설정 태그
invalid-session-url : 세션 타임아웃시 이동할 페이지
max-sessions : 최대 허용 가능한 세션 수
expired-url : 중복 로그인 발생시 이동할 주소. 처음 접속한 세션이 invalidate가 되고 다음 요청시 invalid-session-url로 이동한다.
나중 접속 세션을 차단하고 싶다면 expired-url 대신 error-if-maximum-exceeded를 true로 설정한다.
* <bean>
DB, 암호화 방식 등에 대한 정보를 모르기 때문에 Bean 등록해 정보 전달한다.
- form-login의 login-processing-url로 로그인 요청 들어올 경우 인증 처리한다.
- AuthenticationProvider 인터페이스의 구현부가 처리(작성해야 함)
* <sec:authencation-manager>
authentication-provider : 로그인 처리 클래스를 인증 관리자에 등록한다.로그인 요청 url을 securityContext.xml의 login-processing-url로 변경한다.
로그아웃 요청 url 마찬가지로 logout-url로 변경한다. 이제 로그인, 로그아웃 처리를 Spring Security를 통해 처리할 수 있게 됐다.
인터페이스 구현부 작성
스프링 시큐리티가 사용할 커맨드 객체 생성한다. UserDetails 인터페이스를 구현한다.package kr.co.hucloud.security; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import kr.co.hucloud.security.code.example.member.vo.MemberVO; /** * * @author Admin * 스프링 시큐리티가 관리하는 객체 생성 방법. * */ public class User implements UserDetails{ private MemberVO memberVO; public User(MemberVO memberVO) { this.memberVO = memberVO; } public MemberVO getMemberVO() { return memberVO; } @Override public Collection getAuthorities() { List
authorities = new ArrayList (); authorities.add(new SimpleGrantedAuthority("ROLE_USER")); return authorities; } @Override public String getPassword() { return memberVO.getPassword(); } @Override public String getUsername() { return memberVO.getId(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } 컨트롤러에서는 세션 등록과 나머지 작업을 수행하면 된다.
이때 사용자 정보는 SecurityContextHolder.getContext().getAuthentication.getDetails();를 통해 가져온다. Authentication 구현부에서 setDetails(); 메소드의 결과를 가져오는 것이다.세션에 대한 정보를 로그인시 할당해주는 기존 로직 설정 방법. 이후 작업을 수행하면 로그인, 로그아웃시 보안을 강화할 수 있다.