Spring Security Role 정의

ITWeb/개발일반 2012. 3. 5. 18:03
내가 궁금했던건 이거다.

"ROLE_XXXX" 라는 걸 어디서 정의 하는 거냐?

별로 어렵지도 않은 내용인데.. 직관적인 설명을 잘 찾지 못했다.
결론을 정리 하면.. 바로 spring security 의 설정 부분인 아래 코드이다.
- 막상 내가 보니 어떤 파일인지 궁금해 하시는 분이 계실 것 같아..추가 합니다.
- web.xml 에서 추가한 security 관련 context 파일이 되겠습니다.

[web.xml]

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>

            classpath:applicationContext-business.xml

            /WEB-INF/applicationContext-security.xml

        </param-value>

    </context-param>


[applicationContext-security.xml]

// 바로 아래 코드가 되겠구요.


<authentication-provider>
        <password-encoder hash="md5"/>
        <user-service>
            <user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
            <user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
            <user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
            <user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>


설정에서 보면.. 빨간색 부분이 정의를 하는 부분이다.
소스코드에서는 아래 파일 이다.

- RoleVoter.java 

/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */


package org.springframework.security.access.vote;


import java.util.Collection;


import org.springframework.security.access.AccessDecisionVoter;

import org.springframework.security.access.ConfigAttribute;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.GrantedAuthority;


/**

 * Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix

 * indicating that it is a role. The default prefix string is <Code>ROLE_</code>,

 * but this may be overridden to any value. It may also be set to empty, which

 * means that essentially any attribute will be voted on. As described further

 * below, the effect of an empty prefix may not be quite desirable.

 * <p>

 * Abstains from voting if no configuration attribute commences with the role

 * prefix. Votes to grant access if there is an exact matching

 * {@link org.springframework.security.core.GrantedAuthority} to a <code>ConfigAttribute</code>

 * starting with the role prefix. Votes to deny access if there is no exact

 * matching <code>GrantedAuthority</code> to a <code>ConfigAttribute</code>

 * starting with the role prefix.

 * <p>

 * An empty role prefix means that the voter will vote for every

 * ConfigAttribute. When there are different categories of ConfigAttributes

 * used, this will not be optimal since the voter will be voting for attributes

 * which do not represent roles. However, this option may be of some use when

 * using pre-existing role names without a prefix, and no ability exists to

 * prefix them with a role prefix on reading them in, such as provided for

 * example in {@link org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl}.

 * <p>

 * All comparisons and prefixes are case sensitive.

 *

 * @author Ben Alex

 * @author colin sampaleanu

 */

public class RoleVoter implements AccessDecisionVoter<Object> {

    //~ Instance fields ================================================================================================


    private String rolePrefix = "ROLE_";


    //~ Methods ========================================================================================================


    public String getRolePrefix() {

        return rolePrefix;

    }


    /**

     * Allows the default role prefix of <code>ROLE_</code> to be overridden.

     * May be set to an empty value, although this is usually not desirable.

     *

     * @param rolePrefix the new prefix

     */

    public void setRolePrefix(String rolePrefix) {

        this.rolePrefix = rolePrefix;

    }


    public boolean supports(ConfigAttribute attribute) {

        if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {

            return true;

        }

        else {

            return false;

        }

    }


    /**

     * This implementation supports any type of class, because it does not query

     * the presented secure object.

     *

     * @param clazz the secure object

     *

     * @return always <code>true</code>

     */

    public boolean supports(Class<?> clazz) {

        return true;

    }


    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {

        int result = ACCESS_ABSTAIN;

        Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);


        for (ConfigAttribute attribute : attributes) {

            if (this.supports(attribute)) {

                result = ACCESS_DENIED;


                // Attempt to find a matching granted authority

                for (GrantedAuthority authority : authorities) {

                    if (attribute.getAttribute().equals(authority.getAuthority())) {

                        return ACCESS_GRANTED;

                    }

                }

            }

        }


        return result;

    }


    Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {

        return authentication.getAuthorities();

    }

}


이넘들의 각 Class 간 flow 를 보면.. 아래 그림과 같다.
참고사이트 :  http://static.springsource.org/spring-security/site/docs/3.0.x/reference/authz-arch.html 

이미지 출처 :  http://whiteship.me/?tag=spring-security 



이제 어디서 정의 하는지는 확인을 했으니 이걸 매번 설정파일에 등록해 줄 수는 없으므로 jdbc-user-service 설정을 하는 걸 보도록 하자.

[참고사이트]
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html 
-  http://www.mularien.com/blog/2008/07/07/5-minute-guide-to-spring-security/ 

2.2.3 Using other Authentication Providers

In practice you will need a more scalable source of user information than a few names added to the application context file. Most likely you will want to store your user information in something like a database or an LDAP server. LDAP namespace configuration is dealt with in the LDAP chapter, so we won't cover it here. If you have a custom implementation of Spring Security's UserDetailsService, called "myUserDetailsService" in your application context, then you can authenticate against this using

  <authentication-manager>
    <authentication-provider user-service-ref='myUserDetailsService'/>
  </authentication-manager>
  
        

If you want to use a database, then you can use

  <authentication-manager>
    <authentication-provider>
      <jdbc-user-service data-source-ref="securityDataSource"/>
    </authentication-provider>
  </authentication-manager>
  
        

Where securityDataSource is the name of a DataSource bean in the application context, pointing at a database containing the standard Spring Security user data tables. Alternatively, you could configure a Spring Security JdbcDaoImpl bean and point at that using the user-service-ref attribute:

  <authentication-manager>
    <authentication-provider user-service-ref='myUserDetailsService'/>
  </authentication-manager>

  <beans:bean id="myUserDetailsService"
      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    <beans:property name="dataSource" ref="dataSource"/>
  </beans:bean>
  
        


A.1 User Schema

The standard JDBC implementation of the UserDetailsService (JdbcDaoImpl) requires tables to load the password, account status (enabled or disabled) and a list of authorities (roles) for the user.

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

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

A.1.1 Group Authorities

Spring Security 2.0 introduced support for group authorities in JdbcDaoImpl. The table structure if groups are enabled is as follows:

create table groups (
  id bigint generated by default as identity(start with 0) primary key,
  group_name varchar_ignorecase(50) not null);

create table group_authorities (
  group_id bigint not null,
  authority varchar(50) not null,
  constraint fk_group_authorities_group foreign key(group_id) references groups(id));

create table group_members (
  id bigint generated by default as identity(start with 0) primary key,
  username varchar(50) not null,
  group_id bigint not null,
  constraint fk_group_members_group foreign key(group_id) references groups(id));
        



Database-Backed Authentication

In my case, my application was already configured to use a JDBC DataSource, so pointing Spring Security at my JDBC data source was as easy as modifying the authentication-provider element to reference my already configured Spring bean:

    <authentication-provider>
	    <jdbc-user-service data-source-ref="dataSource"/>
    </authentication-provider>

Now, the immediate question I asked is – OK, what does the convention over configuration assume my database tables look like? If you look at the documentation of the JDBC authentication provider, you would expect to see that information there, but you’d be wrong.

Instead, you have to look at the SQL queries that are hard-coded in the JdbcDaoImpl class and infer the schema structure for yourself. This article has a graphical depiction of the basic schema down in section 5.4.

If you want to configure the queries that are used, simply match the available attributes on the jdbc-user-service element to the SQL queries in the Java class I referenced above. In my example, I wanted to simplify my schema by adding the user’s role directly to the user table. So I modified the XML configuration slightly as follows:

  <jdbc-user-service data-source-ref="dataSource" 
    authorities-by-username-query="select username,authority from users where username=?"/>

This allowed me to put values in the ‘authority’ column like ‘ROLE_ADMIN’ or ‘ROLE_USER’, which translate directly into Spring Security roles!


: