스프링 시큐리티는 스프링 프레임워크의 하위 프로젝트로 웹 보안에 관련된 프레임워크입니다.
스프링 시큐리티의 보안 방법은 권한인증(Authentication)과 권한허가(Authorization)으로 이루어지는데,
사용자가 아이디와 비밀번호를 입력하여 매칭되는지 확인하고, 그 허가가 떨어지면 사용자는 그 인증된 데이터로 계속 보안을 유지하며
인증을 계속하게 됩니다.
스프링 시큐리티가 나온지는 꽤 오랜 시간이 지났지만, 국내에는 그다지 유명하지 않고 소개되어 있는 곳도 많지 않습니다. 따라서 간단한
따라하기 문서를 만들며 소개를 해보도록 하겠습니다. 여기서 소개되는 기능은 첫번쨰에서는 최소한의 기능만을 가지고 구현까지만 할 것이고,
계속 챕터를 나아가면서 기능을 덧붙여가기로 합시다.
1. 환경구성을 위한 파일 받기
우선 파일을 받아야합니다.
2013년 5월 29일 기준으로 스프링은 4.0이 출시되어있고, 시큐리티는 3.2 버전이 출시되어있습니다.
하지만 스프링 4.0은 아직 써보지도 못했으니 그 바로 전버전인 3.2.3 버전으로 작업을 하겠습니다.
스프링은 아래주소에서 받을 수 있습니다. 메이븐을 사용한다면 아래의 dependency를 추가하면 됩니다.
<repository> <id>springsource-repo</id> <name>SpringSource Repository</name> <url>http://repo.springsource.org/release</url> </repository> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.3.RELEASE</version> </dependency> |
스프링 시큐리티는 아래에서 받을 수 있습니다.
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>3.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.1.4.RELEASE</version> </dependency> |
그 이외에도 스프링과 기타 환경설정을 구성하기 위해 아래의 파일들을 추가로 등록했습니다.
commons-dbcp-1.2.2.jar commons-logging-1.1.1.jar commons-pool-1.3.jar aopalliance-1.0.jar //트렌젝션을 위해 등록 jstl-1.2.jar //리졸버 뷰를 위해 등록 sqljdbc4.jar //mssql의 jdbc를 위해 등록 |
2. DB구조 설정
DB는 최소한의 기능만 담아서 아래와 같이 설정했습니다.
userId를 Key로 삼아 권한을 부여 할 것입니다.. 권한은 한 사람이 여러개를 가질 수 있으므로 1:N 으로 만들었습니다.
password는 시큐리티 자체에서 암호화하는 것을 권고하고 있으므로 크기를 넉넉하게 선언하는게 좋습니다.
permit은 승인 여부를 설정하는 것입니다. 1, 0 으로 boolean처럼 설정하면 됩니다.
3. 스프링과 시큐리티의 환경설정
본격적으로 새 웹 프로젝트를 만들고 시작해봅시다.
새 웹 프로젝트를 제작 했다면, web.xml 부터 설정해봅시다.
스프링 시큐리티는 FilterChain을 통해서 서블릿에서 넘어온 데이터들을 필터링합니다. 그러니 우선 이 설정을 추가합시다.
<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> |
그리고 WEB-INF에 스프링과 시큐리티의 설정 파일을 생성합시다.
이름은 각자 마음대로 지을 수 있으나 단순하게 스프링 설정은 spring-servlet.xml , 시큐리티는 security-context 라고 쓰겠습니다.
이 두 파일도 서블릿에 맵핑시켜줘야 하므로 web.xml에 어디있는지 알려줄 설정을 써넣습니다.
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-servlet.xml /WEB-INF/security-context.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> |
다음은 spring-servlet 에 대한 설정입니다. 뷰 리졸버의 패턴을 설정하고, db를 설정합니다. 여기선 mssql 2008을 사용했으므로 각자의
db에 맞춰서 설정을 바꾸면 됩니다. 또한 트렌젝션을 이용하기 위해서 트렌젝션을 위한 설정도 써주었습니다.
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!-- example 패키지의 빈을 자동으로 생성 --> <context:component-scan base-package="com.springSec" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/view/" /> <property name="suffix" value=".jsp"></property> </bean>
<!-- mssql--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/> <property name="url" value="jdbc:sqlserver://localhost:1433;selectMethod=cursor;"/> <property name="username" value="userid"/> <property name="password" value="password"/> </bean>
<!-- 트랜잭션 매니저 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<!-- Annotation 기반 트랜잭션 설정 --> <tx:annotation-driven transaction-manager="transactionManager" /> </beans> |
제일 중요한 security-context의 설정입니다. 좀 기니 세토막으로 잘라서 설명하겠습니다.
1. <Http>
http 안에서는 주소에 대한 설정을 가집니다. 주로 주소를 가로채는 intercept를 설정하여 권한에 맞지 않으면 튕겨내는 기능을 합니다.
use-expressions : intercept-url의 access에 spring EL을 사용. 예를 들면 hasAnyRole() 과 같은 것들
(자세한 내용은 여길 참고 http://blog.naver.com/platinasnow/30168908884 )
intercept-url : 해당주소에 어떤 권한을 가진 사람을 접근시킬 것인가에 대한 설정을 할 수 있음. 더불어 Handler를 설정하여 intercept
했을 때 추가 설정을 해줄 수도 있음.
access="permitAll : 같은 경우는 모든 권한을 전부 접근 허락하겠다는 의미
access="hasAnyRole()" : () 안의 권한만 허락하겠다는 의미
login-page : 로그인을 할 페이지를 설정
default-target-url : intercept 당했을 때 돌아가는 주소이다.
logout invalidate-session : 로그아웃 했을 때 세션을 비움
logout-url : 로그아웃을 시킬 주소
logout-success-url :로그아웃이 성공하면 보내질 페이지
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http auto-config="true" use-expressions="true"> <intercept-url pattern="/loginPage.do" access="permitAll" /> <intercept-url pattern="/makeAccount.do" access="permitAll" /> <intercept-url pattern="/makeAccountSubmit.do" access="permitAll" /> <intercept-url pattern="/**.do" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" /> <form-login login-page="/loginPage.do" default-target-url="/loginPage.do"/> <logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/loginPage.do" /> </http>
|
2. <authentication-manager>
authentication-manager에는 로그인하는 정보들이 넘어오는 곳입니다.
authentication-provider user-service-ref 에는 3. 에서 만든 securityService 라는 bean을 주입하여 로그인 정보를 처리 할 것 입니다.
password-encoder는 비밀번호를 암호화 하는 것입니다. 여기에서는 passwordEncode bean을 선언하여 sha 방식으로 인코딩했습니다.
<authentication-manager> <authentication-provider user-service-ref="securityService" > <password-encoder ref="passwordEncoder" /> </authentication-provider> </authentication-manager>
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/> |
3. securityService
로그인 정보를 처리하기 위해서 만든 bean입니다. 따로 튜닝을 하지 않고 시큐리티에서 지원하는 JdbcDaoImpl을 가지고 db 쿼리만 주입해서
진행하도록 하겠습니다. spring-servlet에서 설정한 datasource를 주입하여 usersByUsernameQuery에 아이디, 비밀번호, 접속허가 순서로 쿼리를
넣어줍니다. 아래의 authoritiesByUsernameQuery 에서는 저장된 권한을 불러오는 쿼리를 작성하면 됩니다. 아이디, 권한 순으로 짜면 되겠습니다.
<beans:bean id="securityService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"> <beans:property name="dataSource" ref="dataSource" /> <beans:property name="usersByUsernameQuery"> <beans:value> SELECT userid,password,permit FROM UserTable WHERE userid=? </beans:value> </beans:property> <beans:property name="authoritiesByUsernameQuery"> <beans:value> SELECT userId,authority FROM Authorities WHERE userId = ? </beans:value> </beans:property> </beans:bean> </beans:beans> |
4. 컨트롤러, DAO, 뷰
이제 거의 끝났습니다. 그럼 우선 로그인페이지와 아이디 생성페이지를 위한 뷰를 위한 컨트롤러를 설정합시다.
여기서 makeAccount.do , makeAccountSubmit.do 는 위의 intercept-url 에서 열어준 페이지이므로 다른 페이지에 접근이 안된다고 이상하게
생각하지 맙시다. 다른 페이지에 접근하려면 페이지에 맞게 권한을 열어줘야 합니다.
@Autowired private LoginDao loginDao;
@RequestMapping("/loginPage.do") public String loginPage(Model model){ return "loginPage"; }
@RequestMapping("/logout.do") public String logout(Model model){ return "redirect:index.do"; }
@RequestMapping("/makeAccount.do") public String makeAccount(Model model){ return "makeAccount"; }
@RequestMapping("/makeAccountSubmit.do") public String makeAccountSubmit(String userId, String password){ loginDao.makeAccount(userId, password); return "redirect:loginPage.do"; } |
Dao는 스프링에서 지원하는 JdbcDaoSupport를 사용해서 간단하게 만들었습니다. 비밀번호를 Insert 할 때 sha방식으로 인코딩해서 집어넣는 것에
유의합시다.
@Autowired private JdbcDaoSupport daoSupport;
@Autowired private PasswordEncoder passwordEncoder;
private void insertUser(String userId, String password) { String sql="use praline " + "INSERT INTO UserTable(userId, password, permit) "+ "VALUES(?, ?, 1)"; daoSupport.getJdbcTemplate().update(sql, userId, passwordEncoder.encodePassword(password, null)); }
private void insertAuthority(String userId) { String sql="use praline " + "INSERT INTO Authorities(userid, authority) "+ "VALUES(?, ?)"; daoSupport.getJdbcTemplate().update(sql.toString(), userId, "ROLE_USER"); }
@Transactional public void makeAccount(String userId, String password) { insertUser(userId, password); insertAuthority(userId); } |
마지막으로 로그인 페이지 뷰입니다.
스프링 시큐리티에 의해서 id는 name을 j_username으로 하여 전송 해야하고, password는 j_password를 써야합니다.
그리고 로그인 도착지점(action)은 j_spring_security_check로 해야합니다.
로그인에 성공하면 <sec:authentication property="Authorities" /> 에 의해서 지금 현재 가지고 있는 권한이 나오게 됩니다.
로그인 하지 않았다면 [ROLE_ANONYMOUS]가 나오게 될 것입니다.
<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <body> <form id="loginForm" method="post" > <p><input type="text" name="j_username" placeholder="ID" required/></p> <p><input type="password" name="j_password" placeholder="Password" required/></p> <Button type="button" onclick="login()" >로그인</Button> <Button type="button" onclick="logout()" >로그아웃</Button> <Button type="button" onclick="makeAccount()">아이디 만들기</Button> <div> <sec:authentication property="Authorities"/> </div>
</form> </body> <script type="text/javascript"> var loginForm = document.getElementById('loginForm'); function login(){ loginForm.action = "j_spring_security_check"; loginForm.submit(); } function logout(){ loginForm.action = "logout.do"; loginForm.submit(); } function makeAccount(){ loginForm.action = "makeAccount.do"; loginForm.submit(); } </script> |
이걸로 간단하게 스프링 시큐리티가 적용 되기만 한 소스가 완성되었습니다. 하지만 이는 스프링 시큐리티의 기능을 최소한으로
사용한 것으로 더 많은 기능들이 있습니다. 앞으로는 이런 기능들을 조금씩 꺼내보겠습니다..
소스코드는 여기에 올려놓았으니 참고해도 좋습니다.
https://github.com/praline0/SpringSecurityExample.git
'날타그리드' 카테고리의 다른 글
트위터 부트스트랩 (0) | 2013.12.18 |
---|---|
부트스트랩 폼빌더 (0) | 2013.12.18 |
안드로이드(Android) 스마트폰 USB 연결 디버깅 환경셋팅 (0) | 2013.11.11 |
Intent - 액티비티 호출(실행) (0) | 2013.11.11 |
[ 안드로이드 ] LogCat 사용법 및 Activity Life Cycle (0) | 2013.11.11 |
다음맵참조 (0) | 2013.11.11 |