2015년 3월 23일 월요일

Spring security url 권한 처리

먼저, Spring Security는 각각의 URL과 ROLE간의 Matching을 어떻게 하고 있는지에 대한 구조에 대한 이해가 필요합니다. Spring Security의 URL-ROLE간의 matching은 다음과 같이 진행됩니다.
  1. FilterInvocationSecurityMetadataSource을 통해 URL/METHOD를 이용한 접근권한이 어떻게 되는지 확인합니다. 이 과정이 매우 중요합니다. 우리가 Spring Security에 각각의 URL별 권한을 hard coding으로 넣어주는 경우에는, 이 MetadataSource가 hard coding된 것과 같은 역활을 하게 됩니다.
  2. FilterInvocationSecurityMetadataSource에서 얻어온 ROLE들과 사용자가 갖은 ROLE을 이용해서 AccessVote를 진행합니다.
이러한 두과정을 거치는 Filter가 존재를 하는데, 이 필터가 아래 그림의 최종적인 Filter인 FilterSecurityInterceptor입니다.

이러한 Filter Chain에서 새로운 Filter를 만들어서 Spring Security에 추가를 할 예정입니다. Spring Security의 FilterSecurityInterceptor에는 모든 인증된 Request만이 접근할 수 있도록 설정을 하고, 그 뒤에 우리가 새롭게 만든 Filter를 통해서 인증을 처리할 계획입니다.

먼저, 새로운 FilterInvocationSecurityMetaSource가 필요합니다. 이는 FilterInvocationSecurityMetadataSource interface를 상속하면 매우 간단하게 구성 가능합니다.
WebSecurityConfigurerAdapter  구현체에 filterSecurity 추가
/**
 * TODO Spring Security 설정 처리
 *
 * @author mike Ryu
 * @date 2015. 3. 09
 * @version 1.0
 */

@Configuration
@EnableWebSecurity
@Import(DatabaseConfig.class)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    //@Autowired private UserDetailsService userDetailService;
    @Autowired private LoginSuccessHandler loginSuccessHandler;
    @Autowired private LoginFailureHandler loginFailureHandler;
    @Autowired private CustomAccessDeniedHandler accessDeniedHandler;
    static SessionRegistry SR;
   
    /*@Override
    protected UserDetailsService userDetailsService() {
        return userDetailService;
    }
   
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
         auth.userDetailsService(userDetailsService());
    }*/
   
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Autowired
    private DataSource dataSource;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
                .antMatchers("/resource/**");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic();

        //sessionManagement 추가
        http
        .sessionManagement()
            //.invalidSessionUrl("/login") // session-management@invalid-session-url
            //.sessionAuthenticationErrorUrl("/login") // session-management@session-authentication-error-url
            .maximumSessions(1) // session-management/concurrency-control@max-sessions 동시 접속 1
                .maxSessionsPreventsLogin(true) // session-management/concurrency-control@error-if-maximum-exceeded
                .expiredUrl("/expired-session") // session-management/concurrency-control@expired-url
                .sessionRegistry(SR); // session-management/concurrency-control@session-registry-ref
        //security form and login authorize처리
        http
            .addFilterBefore(new AjaxSessionTimeoutFilter(),BasicAuthenticationFilter.class)
            .addFilterAfter(new CORSFilter(),AjaxSessionTimeoutFilter.class);
        http
            .exceptionHandling()
                //.accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(customAuthenticationEntryPoint())
                .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/j_spring_security_check")
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .successHandler(loginSuccessHandler)
                //.failureHandler(loginFailureHandler)
                .failureUrl("/error-login")
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/home")
                .and()
            .authorizeRequests()
                //.accessDecisionManager(new CustomAccessDecisionManager())
                .antMatchers( "/apis/**","/main","/test", "/index.jsp", "/home", "/favicon.ico", "/resources/**", "/publish/**").permitAll()
                .antMatchers("/rest/**", "/secure/**", "/manage/**", "/admin/**", "/comment/admin/**").hasAnyRole("1","2","3","4","5","6","7")
                .anyRequest().authenticated()
                .and()
            .csrf().disable();
       
    }
   
    @Bean
    public AuthenticationEntryPoint customAuthenticationEntryPoint() {
        CustomAuthenticationEntryPoint customAuthEntryPoint = new CustomAuthenticationEntryPoint();
        return customAuthEntryPoint;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
          .jdbcAuthentication()
              .dataSource(dataSource)
              .passwordEncoder(passwordEncoder())
              .usersByUsernameQuery(getUserQuery())
              .authoritiesByUsernameQuery(getAuthoritiesQuery());
              //.groupAuthoritiesByUsername(getGroupAuthoritiesByUsernameQuery());
    }
   

    // Only necessary to have access to verify the AuthenticationManager
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
   
    //jdbcTemplate 방식으로 처리 하는 경우 
    private String getUserQuery() {
        return "SELECT lgi_id, lgi_pw, case when opr_use_yn='Y' then 'true' else 'false' end as enabled "
                + "FROM tdc_db.OC_OPR "
                + "WHERE  lgi_id= ?";
    }
      //jdbcTemplate 방식으로 처리 하는 경우 
    private String getAuthoritiesQuery() {
        return "select A.lgi_id, CONCAT('ROLE_',B.ROL_ID) from tdc_db.OC_OPR A, tdc_db.OC_ROL B, tdc_db.OC_OPR_ROL C where A.OPR_ID=C.OPR_ID and  B.ROL_ID=C.ROL_ID and A.lgi_id=?";
    }
    //jdbcTemplate 방식으로 처리 하는 경우 
    private String getGroupAuthoritiesByUsernameQuery() {
        return "select groups.group_id, groups.name, authorities.authority from test.groups, test.users , test.group_users, test.group_authorities, test.authorities, test.user_authorities where group_users.group_id = groups.group_id and users.user_id = group_users.user_id and groups.group_id = group_authorities.group_id and group_authorities.authority_id=authorities.authority_id and users.user_id=?";
    }
   
    @Autowired
    @Bean
    public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception{
       
        // FilterSecurityInterceptor
        FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
        filterSecurityInterceptor.setAuthenticationManager(authenticationManagerBean());
        filterSecurityInterceptor.setAccessDecisionManager(new CustomAccessDecisionManager());
        // SecurityExpressionHandler
        SecurityExpressionHandler<FilterInvocation> securityExpressionHandler = new DefaultWebSecurityExpressionHandler();

        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
        //map.put(new AntPathRequestMatcher("/test"), Arrays.<ConfigAttribute>asList(new SecurityConfig("permitAll")));
        MyFilterSecurityMetadataSource ms = new MyFilterSecurityMetadataSource();
        filterSecurityInterceptor.setSecurityMetadataSource(ms);
        try {
            filterSecurityInterceptor.afterPropertiesSet();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filterSecurityInterceptor;
    }
}
MyFilterSecurityMetadataSource DBurl을 통한 권한 처리 비즈니스 로직 처리
/**
 * TODO Security Filter를 위한 MetadataSource를 통한 url권한 attribute 처리 클래스
 *
 * @author mike Ryu
 * @date 2015. 3. 09
 * @version 1.0
 */
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private final Logger log = LoggerFactory.getLogger(this.getClass());
   
    public List<ConfigAttribute> getAttributes(Object object) {
        FilterInvocation fi = (FilterInvocation) object;
        String url = fi.getRequestUrl();
        List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

        attributes = getAttributesByURL(url);

        return attributes;
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }

    public List<ConfigAttribute> getAttributesByURL(String inputUrl)
    {
        List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

        Connection connection = null;
        String url = "jdbc:mysql://175.113.119.232:3306/tdc_db";
        String driverName = "com.mysql.jdbc.Driver";
        String userName = "tdc_app";
        String password = "app1q2w3e4r!@";
        try{
            Class.forName(driverName).newInstance();
            connection = DriverManager.getConnection(url, userName, password);
            try{
                Statement stmt = connection.createStatement();
                String selectquery = "select concat('ROLE_',B.ROL_ID) as ACCESS from tdc_db.OC_MEN A, tdc_db.OC_ROL_MEN B where A.men_id = B.men_id AND A.MEN_URL = '" + inputUrl +"'";
                log.error("Query: {}", selectquery);
                ResultSet rs = stmt.executeQuery(selectquery);
                while(rs.next()){
                    MyConfigAttribute temp = new MyConfigAttribute();
                    String attr = rs.getString("ACCESS").toString();
                    temp.setAttribute(attr);
                    attributes.add(temp);
                }
            }
            catch(SQLException s){
                System.out.println(s);
            }
            connection.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return attributes;
    }
}

댓글 없음:

댓글 쓰기