티스토리 뷰

Spring Security 도입하기.

프로젝트를 진행함에 있어 인증, 권한에 대한 이슈는 항상 존재하기 마련이다.

복잡한 권한관리를 Spring Security를 이용하면 간단히 해결 할 수 있다.


개발환경

Spring Framework 3.2

Maven 3.0.4

Hibernate 4.3.10

Spring Security 3.2.7


step 1. Spring Security Dependency 추가

spring security를 사용하기 위해 Dependency를 추가한다.

pom.xml

<!-- Security -->

<dependency>

  <groupId>org.springframework.security</groupId>

  <artifactId>spring-security-config</artifactId>

  <version>3.2.7.RELEASE</version>

</dependency>

<dependency>

  <groupId>org.springframework.security</groupId>

  <artifactId>spring-security-web</artifactId>

  <version>3.2.7.RELEASE</version>

</dependency>


step 2. Authentication 구현

로그인 처리로직을 위한 Provider를 생성한다. DB등의 Table에서 사용자 조회 후 

인증에 성공한 경우 권한을 추가하면 된다.


CustomAuthenticationProvider.java

package kr.co.rocksea; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; public class CustomAuthenticationProvider implements AuthenticationProvider { private static final Logger logger = LoggerFactory.getLogger(LoginController.class); @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String user_id = (String)authentication.getPrincipal(); String user_pw = (String)authentication.getCredentials(); logger.info("사용자가 입력한 로그인정보입니다. {}", user_id + "/" + user_pw); if(user_id.equals("test") && user_pw.equals("test")){ logger.info("로그인 성공."); List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>(); roles.add(new SimpleGrantedAuthority("ROLE_USER")); UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user_id, user_pw, roles); result.setDetails(new CustomUserDetails(user_id, user_pw)); return result; }else{ logger.info("사용자 정보가 일치하지 않습니다."); throw new BadCredentialsException("Bad credentials"); } } }


CustomUserDetails.java

package kr.co.rocksea;

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;
 
public class CustomUserDetails implements UserDetails {
 
    private static final long serialVersionUID = -4450269958885980297L;
    private String username;
    private String password;
     
    public CustomUserDetails(String userName, String password)
    {
        this.username = userName;
        this.password = password;
    }
     
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();   
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
         
        return authorities;
    }
  
    @Override
    public String getPassword() {
        return password;
    }
  
    @Override
    public String getUsername() {
        return username;
    }
  
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
  
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
  
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
  
    @Override
    public boolean isEnabled() {
        return true;
    }
}


LoginController.java

package kr.co.rocksea;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
public class LoginController {
     
    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
     
    /**
     * Simply selects the home view to render by returning its name.
     */
    @RequestMapping(value = "login", method = RequestMethod.GET)
    public void login(HttpSession session) {
        logger.info("Welcome login. {}", session.getId());
    }
     
    @RequestMapping(value = "logout", method = RequestMethod.GET)
    public void logout(HttpSession session) {
        CustomUserDetails userDetails = (CustomUserDetails)session.getAttribute("userLoginInfo");
         
        logger.info("success logout. {}, {}", session.getId(), userDetails.getUsername());
         
         
        session.invalidate();
    }
     
    @RequestMapping(value = "login_success", method = RequestMethod.GET)
    public void login_success(HttpSession session) {
        CustomUserDetails userDetails = (CustomUserDetails)SecurityContextHolder.getContext().getAuthentication().getDetails();
         
        logger.info("login success. {}, {}", session.getId(), userDetails.getUsername() + "/" + userDetails.getPassword());
        session.setAttribute("userLoginInfo", userDetails);
    }
     
    @RequestMapping(value = "login_duplicate", method = RequestMethod.GET)
    public void login_duplicate() {    
        logger.info("login_duplicate.");
    }
     
}


login.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<html>
<head>
    <title>로그인페이지</title>  
</head>
  
<body>
<h2>로그인 </h2>
<form name="form" method="post" action="loginProcess">
<table>
    <tr height="40px">
        <td>사용자아이디</td>
        <td><input type="text" name="id"></td>
    </tr>
    <tr height="40px">
        <td>패스워드</td>
        <td><input type="password" name="pw"></td>
    </tr>
</table>
<table>
    <tr>
        <td align="center"><input type="submit" value="로그인"></td>
        <td align="center"><input type="reset" value="리셋"></td>
    </tr>
</table>
</form>
</body>
</html>


step 3. Security 권한 설정

각 접근 URL Pattern에 맞게 권한을 설정 할 수 있다. 기본정책을 로그인이 필요한 

ROLE_USER로(일반사용자)로 설정해두고 몇몇 URL만 로그인 안된 상태로 

접근가능한 IS_AUTHENTICATED_ANONYMOUSLY(익명 사용자)로 설정하여 

관리 할 수 있다. 

기본적으로 Spring Security에서 지원하는 권한은 다음과 같다.

 Authority

 Description 

 ROLE_ANONYMOUS

 모든 사용자

 IS_AUTHENTICATED_ANONYMOUSLY

 익명 사용자

 IS_AUTHENTICATED_FULLY

 인증된 사용자

 IS_AUTHENTICATED_REMEMBERED

 REMEMBERED 사용자

 ROLE_RESTRICTED

 제한된 사용자

 ROLE_USER

 일반 사용자

 ROLE_ADMIN

 관리자


security-context.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"

    xmlns:jee="http://www.springframework.org/schema/jee"

    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd

      http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd

      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

         

  <http auto-config='true' >

    <intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />

    <intercept-url pattern="/login_duplicate" access="IS_AUTHENTICATED_ANONYMOUSLY" />

    <intercept-url pattern="/**" access="ROLE_USER" />

    <form-login login-page="/login"

      username-parameter="id"

      password-parameter="pw"    

      login-processing-url="/loginProcess"

      default-target-url="/login_success"

      authentication-failure-url="/login"

      always-use-default-target='true'

    />

         

    <session-management>

      <concurrency-control max-sessions="1" expired-url="/login_duplicate"/>

    </session-management>

  </http>

     

  <beans:bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

     

  <authentication-manager>

    <authentication-provider ref="customAuthenticationProvider"/>

  </authentication-manager>

     

  <beans:bean id="customAuthenticationProvider" class="com.security.rocksea.CustomAuthenticationProvider"/>

</beans:beans>


step 4. 기타 환경 설정

security-context 파일 경로 추가 및 Listener, Filter를 설정한다.

web.xml
<context-param>

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

  <param-value>

    classpath:spring/application-config.xml

    classpath:spring/security-context.xml

  </param-value>

</context-param>

<!-- Spring Session Listener -->

<listener>

  <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>

</listener>

     

<!-- Spring Security filter -->

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


step 5. 결과화면

[그림 1] 로그인 화면

[그림 2] 로그인 성공 화면


참고 사이트

1. http://goodcodes.tistory.com/entry/Spring-01-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%EC%A6%9D

2. http://www.mkyong.com/spring-security/spring-security-hello-world-example/

댓글