본문 바로가기

백엔드/Spring

Spring Security 설정.

- Spring-security-3.1.0.RC2로 작업.
- 일반적인 Spring 설정은 제외.

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <!-- - Location of the XML file that defines the root application context.
        - Applied by ContextLoaderServlet. -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
             classpath*:/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>


applicationContext-security.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!--?xml version="1.0" encoding="UTF-8"?-->
 
    <!-- Spring Security Configuration Start -->
    <sec:global-method-security pre-post-annotations="enabled"></sec:global-method-security>
 
    <sec:http auto-config="true" use-expressions="true">
        <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter"></sec:custom-filter>
        <sec:session-management session-authentication-strategy-ref="sas"></sec:session-management>
         
        <sec:form-login login-processing-url="/auth/login.view?type=login" login-page="/auth/login.view"></sec:form-login>
         
        <sec:intercept-url pattern="/index.jsp" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/auth/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/join/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/mn/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/images/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/img/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/js/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/css/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/include/**" access="permitAll"></sec:intercept-url>
        <sec:intercept-url pattern="/dwr/**" access="permitAll"></sec:intercept-url>
         
        <sec:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"></sec:intercept-url>
        <sec:intercept-url pattern="/client/**" access="hasRole('ROLE_CLIENT')"></sec:intercept-url>
    </sec:http>
    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider user-service-ref="memberUserDetailService"></sec:authentication-provider>
    </sec:authentication-manager>
     
    <!-- DB 통신을 통해 인증을 가져올 서비스 클래스 -->
    <bean id="memberUserDetailService" class="com.score.www.service.impl.MemberManagerImpl">
    </bean>
 
    <!-- for Spring Security Custom Filter -->
    <bean id="springsecFilterChain" class="org.springframework.web.filter.DelegatingFilterProxy"></bean>
    <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"></bean>
    <bean id="securityContext" class="org.springframework.security.core.context.SecurityContextHolder" factory-method="getContext"></bean>
 
    <bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <property name="loginFormUrl" value="/index.jsp"></property>
            <property name="forceHttps" value="false"></property>
    </bean>
 
    <bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
        <property name="sessionRegistry" ref="sessionRegistry"></property>
        <!--property name="expiredUrl" value="/expired.html" -->
    </bean>
 
    <bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="sessionAuthenticationStrategy" ref="sas"></property>
        <property name="authenticationManager" ref="authenticationManager"></property>
    </bean>
 
    <bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
        <constructor-arg name="sessionRegistry" ref="sessionRegistry"></constructor-arg>
        <property name="maximumSessions" value="1"></property>
        <property name="exceptionIfMaximumExceeded" value="true"></property>
    </bean>
    <!-- Spring Security Configuration End -->
</beans:beans>

MemberManager.java
1
2
3
public interface MemberManager extends UserDetailsService{
    // 구현하고픈 메서드
}

MemberManagerImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Service("memberMgr")
public class MemberManagerImpl extends AbstractGenericManager implements MemberManager{
 
    @Autowired
    // DB와 통신할 DAO
    MemberDao memberDao;
     
    @Override
    // 로그인 메서드
    public Member login(String memberId, String memberPw){
        Member member = (Member) loadUserByUsername(memberId);
        if(member != null){
            if(memberPw.equals(member.getPasswd())) {
                member.setName(member.getAuth().get(0).getAuthority());
                member.setAuthenticated(true);
                return member;
            }
        }
        return null;
    }
         
    @Override
    // UserDetailService 구현체
    public UserDetails loadUserByUsername(String adminId) throws UsernameNotFoundException {
        // Parameters는 커스텀 클래스임.
        Parameters<string, object=""> params = new Parameters<string, object="">();
        params.put("p_id", adminId);
        List<member> list = null;
        try{
            // DAO를 통해 쿼리를 날리고 결과를 받아오는 것.
            list = memberDao.list(params);
            // 유저가 없을경우.
            if(list.size() == 0 || list.size() > 1){
                return null;
            }
        }catch(Exception e){
            StringBuilder logMsg = (new StringBuilder("Username(")).append(adminId).append(") access exception!");
            logger.error(logMsg.toString(), e);
            throw new UsernameNotFoundException(logMsg.toString(), e);
        }
        return list.get(0);
    }
     
     
    public Collection<!--? extends GrantedAuthority--> getAuthorities(Member member) {
        List<simplegrantedauthority> authList = new ArrayList<simplegrantedauthority>();
        Role role = member.getRole();
        authList.add(new SimpleGrantedAuthority(role.getRoleName()));
        return authList;
    }
 
}
</simplegrantedauthority></simplegrantedauthority></member></string,></string,>

Member와 SecurityObject (인증을 저장하고 있을 JPA Entity)
인증 정보를 가지고 있을 객체는 몇가지 객체를 구현해야하는데 이에 필요한 filed가 존재해야만 한다.
JPA에서는 @Entity가 붙은 클래스는 DB와 동기화를 하기 때문에 DB에 없는 filed를 넣을시 에러가 난다.
때문에 SecurityObject를 만들어서 객체 구현에 필요한 filed를 가지게하고
인증 정보를 가지고 있을 객체가 SecurityObject를 상속하는 방식으로 처리 했다.

SecurityObject.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SecurityObject implements Serializable{
 
    private static final long serialVersionUID = 8359426924640562032L;
     
    protected boolean accountNonExpired;
    protected boolean accountNonLocked;
    protected boolean credentialsNonExpired;
    protected boolean accepted;
     
    protected String name;
    protected Object credentials;
    protected Object details;
    protected Object principal;
    protected boolean authenticated;
 
}


인증 정보를 가지고 있을 Member 객체
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/**
 * The persistent class for the members database table.
 *
 */
@Entity
@Table(name="members")
public class Member extends com.score.www.common.domain.SecurityObject implements Serializable, UserDetails, Authentication {
    private static final long serialVersionUID = 1L;
 
    // 이런 저런 filed, ex) name, id, nickname 등
 
    //bi-directional many-to-one association to Role
    // ROLE과 Member 는 1:N의 관계로 설정했고 이를 JPA로 구현한 것.
    @ManyToOne
    @JoinColumn(name="ROLE_SEQ")
    private Role role;
 
    public Member() {
    }
 
    // 일반 filed의 getter/setter
 
    public Role getRole() {
        return this.role;
    }
 
    public void setRole(Role role) {
        this.role = role;
    }
     
    @Override
    public Object getCredentials() {
        return credentials;
    }
 
    @Override
    public Object getDetails() {
        return details;
    }
 
    @Override
    public Object getPrincipal() {
        return principal;
    }
     
    public void setPrincipal(Object principal){
        this.principal = principal;
    }
 
    @Override
    public boolean isAuthenticated() {
        return authenticated;
    }
 
    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        this.authenticated = isAuthenticated;
    }
 
    @SuppressWarnings("unchecked")
    public List<simplegrantedauthority> getAuth(){
        return (List<simplegrantedauthority>) this.getAuthorities();
    }
     
    @Override
    public Collection<!--? extends GrantedAuthority--> getAuthorities() {
        List<simplegrantedauthority> list = new ArrayList<simplegrantedauthority>();
        list.add(new SimpleGrantedAuthority(role.getRoleName()));
        return list;
    }
 
    @Override
    public String getPassword() {
        return this.passwd;
    }
 
    @Override
    public String getUsername() {
        return this.memberName;
    }
 
    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }
 
    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }
 
    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }
 
    @Override
    public boolean isEnabled() {
        return super.accepted;
    }
 
    @Override
    public String getName() {
        return this.name;
    }
 
    public void setName(String authority) {
        this.name = authority;
    }
     
}
</simplegrantedauthority></simplegrantedauthority></simplegrantedauthority></simplegrantedauthority>