ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] SpringBoot Security 폼 로그인 (react, nginx)
    기초/SPRING 2022. 3. 27. 21:35

    *표시는 배경지식

    *인프런 강좌

    https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard

    [구성도]

    더보기

    nginx > 로그인 페이지  ( react:3000 

    >>> 로그인 >>> 로그인처리 ( springboot:8081 )

    >>> 성공,실패 리다이렉트 >>> nginx 주소

     

    *Spring Security 참고자료

    https://spring.io/guides/topicals/spring-security-architecture

    https://godekdls.github.io/Spring%20Security/authentication/

    https://jeong-pro.tistory.com/205

     

    더보기

    SecurityContextHolder :  security가 인증한 내용 + SecurityContext를 포함하고 있고 현재스레드와 securityCotnext를 연결

    로그인이 완료되면 시큐리티 session을 만드는데 Security session은 SecurityContextHolder에 저장

    Session Object는 Authentication Type으로 저장되며 Authentication 내부에서 사용자정보를 UserDetails 인터페이스로 저장

     

    UserDetailsService 인터페이스는 DB와 연결해 User정보를 체크

    AuthenticationProvider 인터페이스에서는 UserDetailsService 에서 리턴한 정보와 사용자가 입력한 정보를 비교

     

    구현할 객체

    WebSecurityConfigurerAdapter Extends Class : 스프링 시큐리티 적용

    UserDetails Implements Class : 사용자의 정보

    UserDetailsService Implements Class :  DB에서 사용자를 가져올 서비스

     

    SecurityConfig Class

    @Configuration
    @EnableWebSecurity //스프링 시큐리티 필터가 스프링 필터체인에 등록
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) 
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
    	@Bean
    	public BCryptPasswordEncoder encodePassword() {
    		return new BCryptPasswordEncoder();
    	}
    
    	@Override
    	protected void configure(HttpSecurity http) throws Exception {
    		http.csrf().disable();
    		http
    			.authorizeRequests()
    				.antMatchers("/comment/commentSave").authenticated()
    				.antMatchers("/user/**").authenticated()
    				.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
    				.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
    				.anyRequest().permitAll()
    				.and()
    			.formLogin()
    				.defaultSuccessUrl("/")
    				.failureUrl("/login?error")
    				.loginPage("/login")
    				.loginProcessingUrl("/dologin")
    				.and()
    			.logout()
    				.logoutSuccessUrl("/")
    		        .deleteCookies("JSESSIONID")
    				.logoutUrl("/dologout");
    	}
    }

     

    PrincipalDetails Class

    public class PrincipalDetails implements UserDetails{
    	private static final long serialVersionUID = 1L;
    	
    	private UserInfo userInfo; // composition
    	
    	//일반로그인
    	public PrincipalDetails(UserInfo userInfo) {
    		this.userInfo = userInfo;
    	}
    	
    	//해당 User의 권한을 리턴하는 곳 
    	@Override
    	public Collection<? extends GrantedAuthority> getAuthorities() {
    		Collection<GrantedAuthority> collect = new ArrayList<>();
    		collect.add(new GrantedAuthority() {
    			
    			@Override
    			public String getAuthority() {
    				return userInfo.getUserRole();
    			}
    		});
    		return collect;
    	}
    
    	@Override
    	public String getPassword() {
    		return userInfo.getUserPassword();
    	}
    
    	@Override
    	public String getUsername() {
    		return userInfo.getInsUserName();
    	}
    
    	@Override
    	public boolean isAccountNonExpired() {
    		//계정이 만료되었는가?
    		return true;
    	}
    
    	@Override
    	public boolean isAccountNonLocked() {
    		//계정이 잠금상태인가?
    		return true;
    	}
    
    	@Override
    	public boolean isCredentialsNonExpired() {
    		//계정 비밀번호가 몇일이 지났나?
    		return true;
    	}
    
    	@Override
    	public boolean isEnabled() {
    		//계정이 활성화 되있나??
    		return true;
    	}
    }

     

    PrincipalDetailsService Class

    @Service
    public class PrincipalDetailsService implements UserDetailsService{
    
    	@Autowired
    	private UserInfoRepository userInfoRepository;
    	
    	@Override
    	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    		if(username == null || username.equals("")) {
    			throw new UsernameNotFoundException(username);
    		}
    		
    		UserInfo userInfo = userInfoRepository.findByInsUserName(username);
    		if(userInfo == null) {
    			throw new UsernameNotFoundException(username);
    		}
    		return new PrincipalDetails(userInfo);
    	}
    }

     

    *react form 전송방식

    import axios from "axios";
    import qs from 'qs';
    
    const axiosConfig = {
        headers:{
            "Content-Type": "application/x-www-form-urlencoded"
        }
    }
    const axiosBody = {
      username:userNameValue,
      userpassword:userPasswordValue
    }
    
    axios
      .post("http://localhost/dologin",
      qs.stringify(axiosBody),
        axiosConfig)
      .then((response) => {
        console.log(response.data);
      })
      .catch((error) => {
        alert('에러가 발생했습니다.');
      });
    };

    json > form 전송타입으로 변경

    Content-type 변경 : application/x-www-form-urlencoded

    body 변환

    qs사용 , qs.stringfy

     

     

    *nginx.conf

    location = / {
            proxy_pass http://127.0.0.1:3000/;
    }
    location = /login {
            proxy_pass http://127.0.0.1:3000/;
    }
    location = /registry {
            proxy_pass http://127.0.0.1:3000/;
    }
    
    location ~ .static/(js|css|media)/(.+)$ {
            proxy_pass http://127.0.0.1:3000;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    location / {
        proxy_pass http://localhost:8081;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    react에서 페이지로 사용되는 location들은 절대경로로 지정

    react build시 나오는 static / js,css,media파일들을 예외로 지정

    나머지 경로를 Spring Boot 경로로 지정

     

     

     

    댓글

Designed by Tistory.