###############################################################
## 작업 #1 프로젝트 준비

## WEB-INF/lib
commons-logging.jar
org.springframework.aop-3.0.1.RELEASE.jar
org.springframework.asm-3.0.1.RELEASE.jar
org.springframework.beans-3.0.1.RELEASE.jar
org.springframework.context-3.0.1.RELEASE.jar
org.springframework.core-3.0.1.RELEASE.jar
org.springframework.expression-3.0.1.RELEASE.jar
org.springframework.transaction-3.0.1.RELEASE.jar
org.springframework.web-3.0.1.RELEASE.jar
spring-security-config-3.0.2.RELEASE.jar
spring-security-core-3.0.2.RELEASE.jar
spring-security-web-3.0.2.RELEASE.jar


###############################################################
## 작업 #2 초간단 HTTP 보안

## web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext*.xml</param-value>
</context-param>

<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>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


## applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<http auto-config='true'>
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
</http>

<authentication-manager>
<authentication-provider>
<user-service>
<user name="jimi" password="jimis" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobs" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>

</beans:beans>


## /index.jsp
<h1>메인 페이지</h1>
<br/>
<a href="/admin">관리자 페이지</a>


## /admin/index.jsp
<h1>Admin Main Page</h1>
Administrator only!!!
<br/>
<a href="/">메인 페이지</a>


###############################################################
## 응용 #1 직접 만든 로그인 폼

## applicationContext-security.xml
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<form-login login-page="/login.jsp"/>


## login.jsp
<h1>로그인 페이지</h1>
<form name='f' action='/j_spring_security_check' method='POST'>
 <table border="1">
    <tr><td>아이디 </td><td><input type='text' name='j_username' value=''></td></tr>
    <tr><td>비밀번호 </td><td><input type='password' name='j_password'/></td></tr>
    <tr><td colspan='2'><input name="submit" type="submit" value="로그인"/></td></tr>
  </table>
</form>


###############################################################
## 응용 #2 로그아웃

## /index.jsp
<br/>
<a href="/j_spring_security_logout">로그아웃</a>


###############################################################
## 응용 #3 사용자 로그인 확인

## index.jsp
<%@page import="org.springframework.security.core.*"%>
<%@page import="org.springframework.security.core.context.*"%>
<%@page import="org.springframework.security.core.userdetails.*"%>
<%
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

String username = null;
boolean isAnonymous = true;
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
isAnonymous = false;
} else {
username = principal.toString();
isAnonymous = true;
}
%>

...

<%
if(isAnonymous) {
%>
<a href="/login.jsp">로그인</a>
<%
} else {
%>
<%=username %>님 반갑습니다!
<br/>
<a href="/j_spring_security_logout">로그아웃</a>
<%
}
%>


###############################################################
## 응용 #4 친절한 안내

## applicationContext-security.xml
<http auto-config='true' access-denied-page="/noAuthorized.jsp">


## noAuthorized.jsp
<h1>접근 권한 없음!!</h1>
<br/>
<a href="/">메인 페이지</a>


###############################################################
## 응용 #5 로그인 실패 메시지

## applicationContext-security.xml
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error"/>


## login.jsp
<%@page import="org.springframework.security.core.*"%>
<%@page import="org.springframework.security.web.authentication.*"%>
<%
boolean loginError = request.getParameter("login_error") != null;

String errorMsg = "none";
if (loginError) {
AuthenticationException ex = (AuthenticationException) session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY);
errorMsg = ex != null ? ex.getMessage() : "none";
}
%>

...

<%
if(loginError) {
if("Bad credentials".equals(errorMsg)) {
%>
<font color="red">아이디나 비밀번호 입력이 틀렸습니다.</font>
<%
} else {
%> 
<font color="red">로그인 에러 : <%=errorMsg %></font>
<%
}
}
%> 


###############################################################
## 응용 #6 로그인 성공 페이지

## applicationContext-security.xml
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error"
default-target-url="/" always-use-default-target="true"/>


###############################################################
## 응용 #7 특정 URL 통과시키기

## applicationContext-security.xml
<intercept-url pattern="/login.jsp" filters="none"/>
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error"
default-target-url="/" always-use-default-target="true"/>


###############################################################
## 응용 #8-1 동시에 1명만 접속(기존 세션 만료)

## applicationContext-security.xml
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error"
default-target-url="/" always-use-default-target="true"/>
<session-management>
<concurrency-control max-sessions="1"/>
</session-management>


###############################################################
## 응용 #8-2 동시에 1명만 접속(로그인 에러)

## applicationContext-security.xml
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
</session-management>


###############################################################
## 응용 #9 사용자 정보 DB에서 가져오기

## postgresql-8.4-701.jdbc4.jar
## org.springframework.jdbc-3.0.1.RELEASE-A.jar


## db
  create table users(
      username varchar(50) not null primary key,
      password varchar(50) not null,
      enabled boolean not null);

  create table authorities (
      username varchar(50) not null,
      authority varchar(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", enabled) VALUES ('bob', 'bobs', true);
INSERT INTO users(username, "password", enabled) VALUES ('jimi', 'jimis', true);

INSERT INTO authorities(username, authority) VALUES ('bob', 'ROLE_USER');
INSERT INTO authorities(username, authority) VALUES ('jimi', 'ROLE_USER');
INSERT INTO authorities(username, authority) VALUES ('jimi', 'ROLE_ADMIN');


## applicationContext-security.xml
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="securityDataSource" />
</authentication-provider>
</authentication-manager>

..

<beans:bean id="securityDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="org.postgresql.Driver" />
<beans:property name="url" value="jdbc:postgresql:security" />
<beans:property name="username" value="security" />
<beans:property name="password" value="security" />
</beans:bean>



/** 심화 **/


###############################################################
## 심화 #1 메서드 권한제어

## com.springsource.org.aopalliance-1.0.0.jar
## com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar
## com.springsource.net.sf.cglib-2.1.3.jar
## org.springframework.web.servlet-3.0.1.RELEASE-A.jar


## web.xml
    <servlet>
        <servlet-name>security</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>security</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>


## security-servlet.xml
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="my.controller" />
<context:annotation-config />

<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>

</beans>


## my.controller.MyController.java
@Controller
public class MyController {

@RequestMapping("/mvcHello")
public void mvcHello() {
}
}


## WEB-INF/view/mvcHello.jsp
<h1>MVC Hello</h1>


## applicationContext-security.xml
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error"
default-target-url="/" always-use-default-target="true"/>

----

## applicationContext-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

...

<context:component-scan base-package="my.service" />

<global-method-security secured-annotations="enabled"/>  


## my.service.MyService.java
@Service
public class MyService {

@Secured("ROLE_ADMIN")
public String getSecurity() {
return "success";
}
}


## my.controller.MyController.java
@Autowired MyService myService;
@RequestMapping("/hasSomeSecurity")
public void hasSomeSecurity(Model model) {
String result = myService.getSecurity();
model.addAttribute("result", result);
}


## index.jsp
<a href="/hasSomeSecurity.do">보안 페이지</a>
<br/>


## WEB-INF/view/hasSomeSecurity.jsp
<h1>무언가 보안요소를 가지고 있는 페이지</h1>
보안결과 : ${result }


###############################################################
## 심화 #2 MyUserDeatilsService

## applicationContext-security.xml
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService"/>
</authentication-manager>

<beans:bean id="myUserDetailsService"
class="my.service.MyUserDetailsService">
<beans:property name="myDao">
<beans:bean class="my.dao.MyDao">
<beans:property name="dataSource" ref="securityDataSource"/>
</beans:bean>
</beans:property>
</beans:bean>


## my.domain.MyUserDetails
public class MyUserDetails implements UserDetails {

private String username;
private String password;
private boolean isEnabled = true;
private boolean isAccountNonExpired = true;
private boolean isAccountNonLocked = true;
private boolean isCredentialsNonExpired = true;
private Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
public MyUserDetails(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return authorities;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return username;
}

@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}

@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}

@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}

@Override
public boolean isEnabled() {
return isEnabled;
}

}


## my.service.MyUserDetailsService
public class MyUserDetailsService implements UserDetailsService {

private MyDao myDao;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
UserDetails userDetails = myDao.getUserDetails(username);
Collection<GrantedAuthority> authorities = myDao.getAuthorities(username);
userDetails.getAuthorities().addAll(authorities);
return userDetails;
}

public void setMyDao(MyDao myDao) {
this.myDao = myDao;
}
}


## my.dao.MyDao.java
public class MyDao extends JdbcDaoSupport {

    public static final String DEF_USERS_BY_USERNAME_QUERY =
        "select username,password " +
        "from users " +
        "where username = ?";
    public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
        "select username,authority " +
        "from authorities " +
        "where username = ?";
    
public UserDetails getUserDetails(String username) {
        List<UserDetails> userDetailsList = getJdbcTemplate().query(DEF_USERS_BY_USERNAME_QUERY, new String[] {username}, new RowMapper<UserDetails>() {
            public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
                String username = rs.getString(1);
                String password = rs.getString(2);
                return new MyUserDetails(username, password);
            }

        });
        
        if(userDetailsList.size() == 1)
        return userDetailsList.get(0);
        else 
        return null;
}

public Collection<GrantedAuthority> getAuthorities(String username) {
        return getJdbcTemplate().query(DEF_AUTHORITIES_BY_USERNAME_QUERY, new String[] {username}, new RowMapper<GrantedAuthority>() {
            public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
                String roleName = rs.getString(2);
                return new GrantedAuthorityImpl(roleName);
            }
        });
}

}


###############################################################
## 심화 #3 XML-Based Configuration

## applicationContext-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/login.jsp" filters="none"/>
<sec:filter-chain pattern="/sessionExpired.jsp" filters="none"/>
<sec:filter-chain pattern="/**" filters="
     securityContextPersistenceFilter,
     logoutFilter,
     formLoginFilter,
     exceptionTranslationFilter,
     filterSecurityInterceptor" />
</sec:filter-chain-map>
</bean>

<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name="securityContextRepository">
<bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
</property>
</bean>
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg><bean class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler"/></constructor-arg>
<constructor-arg><bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/></constructor-arg>
<property name="filterProcessesUrl" value="/j_spring_security_logout"></property>
</bean>

<bean id="formLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/"></property>
<property name="alwaysUseDefaultTargetUrl" value="true"></property>
</bean>
</property>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.jsp?login_error"></property>
</bean>
</property>
<property name="sessionAuthenticationStrategy">
<bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg>
<bean class="org.springframework.security.core.session.SessionRegistryImpl"></bean>
</constructor-arg>
<property name="maximumSessions" value="1"></property>
</bean>
</property>
</bean> 

<bean id="exceptionTranslationFilter"
    class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/noAuthorized.jsp"/>
</bean>
</property>
</bean>

<bean id="filterSecurityInterceptor"
       class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource">
<sec:filter-security-metadata-source>
<sec:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<sec:intercept-url pattern="/**" access="ROLE_USER" />
</sec:filter-security-metadata-source>
</property>
</bean>

<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="myUserDetailsService" />
</bean>
</property>
</bean>
<bean id="myUserDetailsService"
class="my.service.MyUserDetailsService">
<property name="myDao">
<bean class="my.dao.MyDao">
<property name="dataSource" ref="securityDataSource"/>
</bean>
</property>
</bean>

<bean id="securityDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql:security" />
<property name="username" value="security" />
<property name="password" value="security" />
</bean>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.ConsensusBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleVoter"></bean>
</list>
</property>
</bean>

<context:component-scan base-package="my.service" />

<sec:global-method-security secured-annotations="enabled"></sec:global-method-security>  
반응형

+ Recent posts