이번 포스팅은 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 랑 비교해서 어떤 식으로 로그인돼있나 판단하는 건지.. 전혀 모르겠다 시큐리티 쓰면 로그인 쉽다고 해서 한번 써보려다가 하루 종일 뭐하는 짓인지 모르겠다
'intellij +springboot' 카테고리의 다른 글
[Spring] 스프링 부트 배포하기 (War 파일로 빌드) (0) | 2023.11.28 |
---|---|
[intelliJ]인텔리제이에서 이클립스 프로젝트 import하기 (2) | 2023.10.24 |
[intellij] 그래들 스프링부트 프로젝트 화면으로 데이터 가져오기(gradle + springboot) - 4 (1) | 2022.10.05 |
[intellij]그래들 스프링부트 프로젝트 쿼리 적용 해보기(gradle + springboot) - 3 (2) | 2022.10.05 |
[intellij]그래들 스프링부트 프로젝트 MariaDB 연결(gradle + springboot) - 2 (2) | 2022.10.04 |