2016년 12월 12일 월요일

98day / Final Project / SPRING Security

http://howtodoinjava.com
오랜만에 수업을!
Security는 조장이 주도해서 프로젝트에 적용하기로 하였으나
일단 수업을 진행하였다. 겨우 30분이지만 졸아버렸다는..

Maven (pom.xml)

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>3.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>        
    <artifactId>spring-security-taglibs</artifactId>
    <version>3.2.5.RELEASE</version>
</dependency>  
cs


spring Security 설정 xml

<!-- Spring Security 인증처리로직를 정의한 클래스 -->
<bean name="customizedAuthenticationProvider"
    class="org.kosta.wikipictures.security.MemberAuthenticationProvider" />
    
<!-- Annotation 기반 spring security를 사용하기 위한 설정 -->
<sec:global-method-security secured-annotations="enabled" />
    
<!-- security 적용대상 제외:모든 디렉토리의 js 확장자 파일은 제외하고자 할 때 -->    
<sec:http pattern="/**/*.js" security="none"></sec:http>        
    
    
<!-- 이전페이지 기억하기 위한 Bean -->
<bean id="customLoginSuccessHandler" class="org.kosta.wikipictures.security.CustomLoginSuccessHandler" />
<!-- 
      XML 기반일 경우 아래와 같이 설정할 수 있다 
           
      만약 회원 전용시스템일 경우
      모든 요청에 대해 access = "hasRole('ROLE_MEMBER') 
      또는 access = "hasAnyRole('ROLE_MEMBER','ROLE_ADMIN')" 을 주고 
      특정 서비스에 대해서 모든 사용자에게 접근할 수 있도록 하면 된다 
      <sec:intercept-url pattern="/register.do" access="permitAll" />
  -->
<sec:http use-expressions="true">        
    <!-- <sec:intercept-url pattern="/admin_*" access="hasRole('ROLE_ADMIN')" />
    <sec:intercept-url pattern="/m_*" access="hasAnyRole('ROLE_MEMBER','ROLE_ADMIN')" />        
    <sec:intercept-url pattern="/**" access="permitAll" /> -->
    <!-- 
            login-page : 로그인 페이지 주소를 지정
            username-parameter :  로그인 페이지 form에 있는 username(아이디)의 name
            password-parameter :  로그인 페이지 form에 있는 password의 password
            login-processing-url :  로그인 페이지 form action에 입력할 주소 지정
            default-target-url :  로그인 성공시 이동 주소 
            authentication-failure-url :  로그인 실패인 경우 이동 주소 
     -->
    <sec:form-login  login-page="/auth_error.do"
        login-processing-url="/login.do" authentication-failure-url="/member/login_all_fail.do"
        username-parameter="id" password-parameter="password" 
        authentication-success-handler-ref="customLoginSuccessHandler"/>
    <sec:logout logout-url="/logout.do" logout-success-url="/home.do" />
</sec:http>
    
<!-- 비밀번호 암호화를 위한 설정 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
    <!-- <constructor-arg name="strength" value="20"></constructor-arg> -->
</bean>
<sec:authentication-manager>
    <sec:authentication-provider ref="customizedAuthenticationProvider" >        
    </sec:authentication-provider>        
</sec:authentication-manager>    
cs


Security 적용

public class CustomLoginSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {
    public CustomLoginSuccessHandler() {
    }
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws ServletException, IOException {
        super.setDefaultTargetUrl(request.getHeader("Referer"));
        super.onAuthenticationSuccess(request, response, authentication);
    }
}
public class MemberAuthenticationProvider implements AuthenticationProvider {
    @Resource
    private MemberService memberService;
    // 비밀번호 암호화처리를 위한 객체를 주입받는다
    @Resource
    private BCryptPasswordEncoder passwordEncoder;
    @Override
    /*Authentication authenticate(Authentication authentication) throws AuthenticationException
     * -실제 인증 처리
     *    - 규칙
     *     1. 파라미터로 전달받은 Authentication 객체에 대해 인증처리를 지원하지 않으면 null을 리턴한다.
     *     2. Authentication 객체를 이용한 인증에 실패하면 AuthenticationException 발생시킨다.
     *     3. 인증에 성공하면, 인증 정보를 담은 Authentication 객체를 만들어 return 한다.
     */
    /**
     * 사용자가 화면에서 로그인을 하면 아래의 메서드가 실행된다. 
     * 매개변수 : 인증시 필요한 정보 - Authentication ( 사용자가 입력한 ID , PASSWORD가 저장되어 있음 ) 
     * 리턴 : 인증한 정보를 가진 Authentication     * 
     * 매개변수에 전달된 Authentication객체를 받아 인증처리를 한뒤 인증한 정보를 Authentication에 담아 리턴
     */
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 파라미터로 전달받은 Authentication 객체에 대해 인증처리를 지원하지 않으면 null을 리턴한다.
        if (!supports(authentication.getClass())) {
            return null;
        }
        // 사용자 정보 DB로 부터 조회(UserDetailsService에서 했던 작업)
        String username = authentication.getName();// 사용자가 로그인시 입력한 ID 반환
        MemberVO member = memberService.findMemberById(username);
        if (member == null) {
            throw new UsernameNotFoundException("회원 아이디가 존재하지 않습니다");
        }
        String password = (String) authentication.getCredentials();// 사용자가 입력한
                                                                    // 패스워드 반환
        패스워드 비교
          /* 비밀번호 암호화를 이용할 경우 
         이용자가 로그인 폼에서 입력한 비밀번호와 DB로부터 가져온 암호화된 비밀번호를 비교한다 */
        if (!passwordEncoder.matches(password, member.getPassword()))
            throw new BadCredentialsException("비밀번호 불일치");

        사용자 권한 조회
        List<AuthoritiesVO> list = memberService.selectAuthorityByUsername(username);
        if (list.size() == 0) {
            throw new UsernameNotFoundException("아무 권한이 없습니다.");
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        for (AuthoritiesVO au : list) {
            authorities.add(new SimpleGrantedAuthority(au.getAuthority()));
        }
        /****************************************
         * 여기까지 왔으면 인증 완료 - Authentication객체 생성해서 리턴
         ***************************************/
        Authentication auth = new UsernamePasswordAuthenticationToken(member, password, authorities);
        System.out.println("로그인 OK~" + auth);
        return auth;
    }
    @Override
    public boolean supports(Class<?> authentication) {
        // Class객체.isAssignableFrom(Class객체) 같은 타입의 객체를 담을 수 있는지 체크ㅡ - 둘이 같은
        // class로 부터 생성된 Class객체인지 체크
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
cs

Security를 적용하며 권한과 비밀번호 암호화 관련해서 이슈가 엄청났다.
기존에 VO나 DB쪽에서도 나름 준비한다고 준비했지만 부족함이 많았다.
Security 적용을 위해 DB에서 View까지 고칠 곳이 굉장히 많아 포기한 팀도 많았지만
우리 팀은 조장주도하에 결국 성공했다.(거의 캐리수준)
수료 후에도 지속적인 공부를 해야겠다. 

0 개의 댓글:

댓글 쓰기