먼저, Spring Security는 각각의 URL과 ROLE간의 Matching을 어떻게 하고 있는지에 대한 구조에 대한 이해가 필요합니다. Spring Security의 URL-ROLE간의 matching은 다음과 같이 진행됩니다.
FilterInvocationSecurityMetadataSource
을 통해 URL/METHOD를 이용한 접근권한이 어떻게 되는지 확인합니다. 이 과정이 매우 중요합니다. 우리가 Spring Security에 각각의 URL별 권한을 hard coding으로 넣어주는 경우에는, 이 MetadataSource가 hard coding된 것과 같은 역활을 하게 됩니다.FilterInvocationSecurityMetadataSource
에서 얻어온 ROLE들과 사용자가 갖은 ROLE을 이용해서 AccessVote를 진행합니다.
이러한 두과정을 거치는 Filter가 존재를 하는데, 이 필터가 아래 그림의 최종적인 Filter인 FilterSecurityInterceptor입니다.
이러한 Filter Chain에서 새로운 Filter를 만들어서 Spring Security에 추가를 할 예정입니다. Spring Security의 FilterSecurityInterceptor에는 모든 인증된 Request만이 접근할 수 있도록 설정을 하고, 그 뒤에 우리가 새롭게 만든 Filter를 통해서 인증을 처리할 계획입니다.
먼저, 새로운 FilterInvocationSecurityMetaSource가 필요합니다. 이는
WebSecurityConfigurerAdapter 구현체에 filterSecurity 추가
FilterInvocationSecurityMetadataSource
interface를 상속하면 매우 간단하게 구성 가능합니다.
/**
*
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 DB의 url을 통한 권한 처리 비즈니스 로직 처리
/**
* 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;
}
}
|
댓글 없음:
댓글 쓰기