본문 바로가기

intellij +springboot

[intellij] 그래들 스프링부트 프로젝트 스프링 시큐리티(Spring Security) 로그인 해보기(gradle + springboot) - 5

 

 

이번 포스팅은 Spring Boot + Gradle + Security 를 이용한 로그인 구현하도록 하겠습니다!

 

build.grdle 에 의존성 추가

    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.security:spring-security-test'

 

 

 

우선 아이디와 비밀번호를 받기 위한 기본 폼 main.html

<div class="login">
    <div>
        <p>ID</p>
        <input id="userId" type="text">
        <p>PW</p>
        <input id="userPw" type="password">
        <button id="login" onclick="fn_loginSubmit()">Log-in</button>
    </div>
</div>

// 시큐리티에서 폼데이터 전송밖에 못받는것 같아서 따로 만들었다..
<form id="LoginPageForm" method="post">
	//name 은 .usernameParameter,.passwordParameter시큐리티 설정이랑 맞춘다
    <input type="hidden" id="username" name="userId" value=""/>
    <input type="hidden" id="password" name="userPw" value=""/>
</form>

.js

    function fn_loginSubmit() {
		//url은 시큐리티 .loginProcessingUrl 랑 맞춤
        $('#LoginPageForm').attr('action', '/login_pro_do');
		// 윗쪽 데이터 값을 가져옴
        $("#username").val($("#userId").val());
        $("#password").val($("#userPassword").val());

        $("#LoginPageForm").submit();

    }

 

로그인 성공 후 넘어갈 페이지 statepage.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div> 로그인 후 페이지</div>
</body>
</html>

 

 

패키지 안에  > config > SecurityConfig 생성

 

이 글에서는 로그인 실패 혹은 인증실패 페이지는 안 만든다 각각 설정한

   .failureUrl("/login/fail") 
  .exceptionHandling().accessDeniedPage("/login/denied") 에서 매칭되게 컨트롤러에서 처리해 주면 됨

package com.example3.demo3.config;

import com.example3.demo3.service.YjLoginService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 스프링 시큐리티가 동작하지 않을 경로나 파일등을 설정 -> css, image, js 등의 경로를 지정
    // 부트 프로젝트는 /resource/static에 정적 파일이 들어간다고 정해져있기 때문에 불필요한 설정
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/images/**","/font/**","/html/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/**").permitAll() // permitAll 인증 없이 접근 가능
                .and()
                .formLogin()// form 로그인 인증 기능이 작동함
                .loginPage("/login/form")// 사용자 정의 로그인 페이지, default: /login
                .usernameParameter("userId")// 아이디 파라미터명 설정, default: username
                .passwordParameter("userPw")// 패스워드 파라미터명 설정, default: password
                .loginProcessingUrl("/login/perform")// 로그인 Form Action Url, default: /login
                .defaultSuccessUrl("/login/success")// 로그인 성공 후 이동 페이지
//                .successHandler( ...생략... ) // 로그인 성공 후 핸들러
//                .failureHandler( ...생략... ) // 로그인 실패 후 핸들러
                .failureUrl("/login/fail") // 로그인에 실패하면 어느 페이지로 이동
                .and()
                .logout()// logout 관련 설정을 진행할 수 있도록 돕는 LogoutConfigurer<> class를 반환
//                .logoutUrl() // client에서 SpringSecurity에게 logout을 요청하기 위한 url을 설정하는 메소드
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login/logout") //로그아웃 성공 시 사용자가 redirect될 url을 지정하는 메소드
                .invalidateHttpSession(true)
                .and()
                .exceptionHandling().accessDeniedPage("/login/denied")
                .and()
                .csrf().disable(); //csrf 를 안쓸 경우
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

// 서비스는 아래에서 만듬
    @Bean
    public YjLoginService loginService() {
        return new YjLoginService();
    }
}

 

- WebSecurity filter 구현

* 해당 경로에는 security 가 모두 무시할 수 있도록 설정

* 기본 경로는 resources/static

 

- HttpSecurity 구현

* http request 에 대한 보안 설정

* authorizeRequests() : request 에따라 접근 제어

 

 표현식  설명
 antMatchers("/admin/**").hasRole("ADMIN") /admin 경로의 모든 경로는 ADMIN 권한 사용자만 접근 
antMatchers("/**").permitAll()  모든 경로에 권한없이 접근 가능 
 .anyRequest().authenticated() 모든 요청에 인증된  

            

         * formlogin() form 로그인 기반으로 인증설정 

 

 표현식 설명 
loginPage("/login/form") 로그인 페이지
usernameParameter("userId") 커스텀 파라메터 default : username 
passwordParameter("password") 커스텀 파라메터 default : password
loginProcessingUrl("/login/perform") action 에 동일한 url 적용 시  AuthenticationManager 상속 구현
defaultSuccessUrl("/index") 로그인 성공
failureUrl("/login/fail") 로그인 실패 
successHandler() 로그인 성공 이후 호출 되는 함수 
failureHandler() 로그인 실패 이후 호출 되는 함수 


추가 *

session-management

  • invalid-session-url : invalid session(세션 타임아웃 등) 이동 url
  • max-sessions : 최대 허용 세션 
  • expired-url : 중복 로그인 시 이동 URL (최초 접속 세션이 사라지고 다음 요청시 invalid-session-url로 이동)
  • error-if-maximum-exceeded : max-sessions을 넘었을 때 접속한 세션을 실패 처리할지 여부 
  • (expired-url중 1개만 사용)

 

AuthenticationProvider.

잘은 몰라도 controller 마냥 .loginProcessingUrl("/login/perform") 에 설정된 url 로 전송하면 데이터를 받는다

AuthenticationProvider 를 implement 해서 내 DB에 있는 계정이랑 매칭시켜서 로그인 하는 건가.. 모르겠다

package com.example3.demo3.controller;

import com.example3.demo3.service.YjLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
   
    @Autowired
    YjLoginService loginService;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        System.out.println("인증 시작");
     
        String userId = (String)authentication.getPrincipal();
        String userPassword = (String)authentication.getCredentials();

        //로그인 로직 구현
        UserDetails user = loginService.loadUserByUsername(userId);

        return new UsernamePasswordAuthenticationToken(user.getUsername(), userPassword, user.getAuthorities());
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
}

 

 

service..

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class YjLoginService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println(" 인증로직 시작");
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
        return new User(username, username,roles);
    }
}

 

controller


    @RequestMapping("/main")
    public  String mian2(Model model){
        System.out.println("zzzzz");
        return "main";
    }
    
  //  상단 configure 에 .defaultSuccessUrl("/state") 같게 설정
    @RequestMapping("/state")
    public  String statego(Model model, Authentication authentication){
        System.out.println("authentication !!!!!!!");
        System.out.println(authentication);
        return "statepage";
    }

 

 

 

 

 

 

우선 여기까지 만들면 로그인 페이지로 잘 넘어가 진다.. .security.core 에 UserDetails, User 가 이미 만들어져 있고

내 DB 랑 비교해서 어떤 식으로 로그인돼있나 판단하는 건지.. 전혀 모르겠다 시큐리티 쓰면 로그인 쉽다고 해서 한번 써보려다가 하루 종일 뭐하는 짓인지 모르겠다

 

 

 

출처 : https://hhseong.tistory.com/173?category=748481