관리 메뉴

제뉴어리의 모든것

spring security 중복 로그인 방지 본문

Spring Boot/Spring Security

spring security 중복 로그인 방지

제뉴어리맨 2021. 3. 30. 15:29

WebSecurityConfigurerAdapter 상속 받은 SecurityConfig 클래스의 구현 부분.

   
       @Override
    protected void configure(HttpSecurity http) throws Exception {
   		http.sessionManagement()
                .maximumSessions(1) /* session 허용 갯수 */
                .expiredUrl("/member/login") /* session 만료시 이동 페이지*/
                .maxSessionsPreventsLogin(false); /* 동일한 사용자 로그인시 x, false 일 경우 기존 사용자 session 종료*/
                
                    }

 

HttpSecurity의 여러가지 설정 예. (protected void configure(HttpSecurity http) 부분 참조할것)

package com.example.demo.config;
 
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 
import com.example.demo.entity.com.ComAuthGrpMenuMap;
import com.example.demo.entity.com.ComMenuMngt;
import com.example.demo.security.SecurityAccessDeniedHandler;
import com.example.demo.security.SecurityAuthenticationFailureHandler;
import com.example.demo.security.SecurityAuthenticationProvider;
import com.example.demo.security.SecurityAuthenticationSuccessHandler;
import com.example.demo.security.SecurityLogoutSuccessHandler;
import com.example.demo.security.SecuritySessionExpiredStrategy;
import com.example.demo.security.SecurityUserDetailsService;
import com.example.demo.service.UserService;
 
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
 
  @Autowired private SecurityAuthenticationSuccessHandler securityAuthenticationSuccessHandler;
  @Autowired private SecurityAuthenticationFailureHandler securityAuthenticationFailureHandler;
  @Autowired private SecurityLogoutSuccessHandler securityLogoutSuccessHandler;
  @Autowired private SecurityAccessDeniedHandler securityAccessDeniedHandler;
  @Autowired private SecuritySessionExpiredStrategy securitySessionExpiredStrategy;
 
  @Autowired private SecurityUserDetailsService securityUserDetailsService;
  @Autowired private SecurityAuthenticationProvider securityAuthenticationProvider;
  @Autowired private UserService userService;
 
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/", "/index.html", "/resources/**", "/caution/**");
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
 
      http.formLogin()
        .loginPage("/login_form.html").permitAll() // 로그인 페이지
        .loginProcessingUrl("/auth") // 로그인 처리 URL
          .usernameParameter("loginid").passwordParameter("passwd")
          .successHandler(securityAuthenticationSuccessHandler.setDefaultUrl("/dashboard/main.html"))
          .failureHandler(securityAuthenticationFailureHandler)
          .and();
 
      http.logout()
        .logoutRequestMatcher(new AntPathRequestMatcher("/common/logout.html"))// 로그아웃 URL
        .logoutSuccessHandler(securityLogoutSuccessHandler.setDefTargetUrl(("/login_form.html")))
        .clearAuthentication(true)
        .invalidateHttpSession(true).and();
 
      http.exceptionHandling()
        .accessDeniedHandler(securityAccessDeniedHandler.setDefaultTargetUrl("/caution/access_denied.html")).and(); // 불량 접근 처리
 
      Collection<Matcher> matchers = getAuthUrlAndRols();
      for(Matcher matcher : matchers){
        http.authorizeRequests().antMatchers(matcher.url).access(matcher.roles);
      }
      http.authorizeRequests().anyRequest().authenticated().and();
 
      http.sessionManagement()
        .maximumSessions(1)  // 같은 아이디로 1명만 로그인 할 수 있음
        .maxSessionsPreventsLogin(false) // 신규 로그인 사용자의 로그인이 허용되고, 기존 사용자는 세션아웃 됨
        .expiredSessionStrategy(securitySessionExpiredStrategy.setDefaultUrl("/caution/session_out.html"))
        .and();
    }
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      securityAuthenticationProvider.setSecurityUserDetailsService(securityUserDetailsService);
        securityAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        auth.authenticationProvider(securityAuthenticationProvider);
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance(); // 개발용 패스워드 암호화 모듈(비 암호화)로써 실제 사용시 변경해 주어야 한다...
    }
 
    /**
     * 권한 URL와 그룹 맵핑
     *
     * @return
     * @author "fishingday"
     * @see
     */
    private Collection<Matcher> getAuthUrlAndRols(){
        Collection <Matcher> matchers = new ArrayList<>();
        List<ComMenuMngt> comMenuMngtList = userService.findAllComMenuMngtAndAuthGrpMngt();
 
        for(ComMenuMngt comMenuMgt : comMenuMngtList){
         Matcher matcher = new Matcher();
         String menuUrl = comMenuMgt.getMenuUrl();
         matcher.url = menuUrl.substring(0, menuUrl.lastIndexOf("/")) + "/**";
         StringBuilder sb = null;
 
         if(comMenuMgt.getComAuthGrpMenuMaps() == null) continue;
         if(comMenuMgt.getComAuthGrpMenuMaps().size() > 1){
             for(ComAuthGrpMenuMap comAuthGrpMenuMap : comMenuMgt.getComAuthGrpMenuMaps()){
              if(sb == null){
               sb = new StringBuilder("hasAnyRole('ROLE_G").append(comAuthGrpMenuMap.getComAuthGrpMngt().getAuthGrpMngtSeq()).append("'");
              }else{
               sb.append(" ,'ROLE_G").append(comAuthGrpMenuMap.getComAuthGrpMngt().getAuthGrpMngtSeq()).append("'");
              }
             }
             sb.append(")");
         }else{
          sb = new StringBuilder("hasRole('ROLE_G").append(comMenuMgt.getComAuthGrpMenuMaps().get(0).getComAuthGrpMngt().getAuthGrpMngtSeq()).append("')");
         }
 
         matcher.roles = sb.toString();
         matchers.add(matcher);
        }
 
        return Collections.unmodifiableCollection(matchers);
       }
 
  class Matcher {
    String url;
    String roles;
  }
}

 

 

두가지를 사용하여 합친 결과물

package org.zerock.member_board.config;

import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.context.SecurityContextRepository;
import org.zerock.member_board.service.CustomOAuth2UserService;
import org.zerock.member_board.service.MemberService;
import org.zerock.member_board.service.MemberServiceImpl;

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final CustomOAuth2UserService customOAuth2UserService;

    private final MemberServiceImpl memberService;
    private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);



    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .headers().frameOptions().disable()
                .and()
                .authorizeRequests()
                .antMatchers("/", "/replies/**","/board/list","/board/read","/css/**","/login/**",
                        "/vendor/**","/modal/**","/member/**","/login/**",
                        "/review/**","/img/**","/attend/getAttend","/websocket/**",
                        "/ws_sock/**","/chat/**","/html/**","/webjars/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/member/login")
                .defaultSuccessUrl("/")
                .and()
                .logout()
                .logoutSuccessUrl("/member/login")
                .and()
                .oauth2Login().defaultSuccessUrl("/")
                .userInfoEndpoint().userService(customOAuth2UserService)
                .and();

        http.sessionManagement()
                .maximumSessions(1)
                .expiredUrl("/member/login")
                .maxSessionsPreventsLogin(false);

    }

    public void configure(AuthenticationManagerBuilder auth) throws Exception { // 9
        auth.userDetailsService(memberService)
                // 해당 서비스(userService)에서는 UserDetailsService를 implements해서
                // loadUserByUsername() 구현해야함 (서비스 참고)
                .passwordEncoder(new BCryptPasswordEncoder());


    }



}

http.sessionManagement() 를 따로 설정함.

 

 

 

 

 

출처 : springboot 2.x spring security 중복로그인 방지, logout 시 session 삭제 안될때 처리 :: 2010년 5월 1일, 2막 (novasky.net)

 

springboot 2.x spring security 중복로그인 방지, logout 시 session 삭제 안될때 처리

출처 : aljjabaegi.tistory.com/508 springboot 2.x spring security 중복로그인 방지, logout 시 session 삭제 안될때 처리 springboot 2.x spring security 중복로그인 방지, logout 시 session 삭제 ..

www.novasky.net

두번째 바람 :: 06. 스프링시큐리티 설정 (tistory.com)