1) SpringBoot
1-1) Security 예제
1-1-1) 로그인, 로그아웃
1) SpringBoot
1-1) Security 예제
[test_security] 안에 build.gradle 파일 생성 후 아래와 같이 작성
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.2'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'net.datasa'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
[src/main/resources] - [templates] 안에 application.properties 파일 내용 아래와 같이 작성
spring.application.name=test_security
#접속 포트번호
server.port=8888
#Context Path
server.servlet.context-path=/
#Logback 사용. 전체를 대상으로 로깅 레벨 지정
#error>warn>info>debug>trace
logging.level.root=info
#특정 패키지를 대상으로 로깅 레벨 지정
logging.level.net.datasa.test=debug
1-1-1) 로그인, 로그아웃
(1) [src/main/java] - [net.datasa.test] 안에 HomeController.java 파일 생성 후 아래와 같이 작성
package net.datasa.test;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class HomeController {
// 메인화면
@GetMapping({"", "/"})
public String home() {
return "home";
}
@GetMapping("view1")
public String view1() {
return "view1";
}
@GetMapping("view2")
public String view2(@AuthenticationPrincipal AuthenticatedUser user) {
log.debug("사용자 아이디 : {}", user.getId());
return "view2";
}
// 로그인 form으로 이동
@GetMapping("loginForm")
public String loginForm() {
return "loginForm";
}
}
(2) [src/main/resources] - [templates] 안에 home.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>test_security</title>
</head>
<body>
<h1>[ test_security ]</h1>
<p>
<a href="view1">view1</a>
</p>
<p sec:authorize="isAuthenticated()">
<a href="view2">view2</a>
</p>
<p sec:authorize="not isAuthenticated()">
<a href="loginForm">로그인</a>
</p>
<p sec:authorize="isAuthenticated()">
<a href="logout">로그아웃</a>
</p>
</body>
</html>
(3) [src/main/resources] - [templates] 안에 view1.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>view1</title>
</head>
<body>
<h1>[ view1 ]</h1>
<p>
<img src="images/squ.jpg">
</p>
</body>
</html>
(4) [src/main/resources] - [templates] 안에 view2.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>view2</title>
</head>
<body>
<h1>[ view2 ]</h1>
</body>
</html>
(5) [src/main/java] - [net.datasa.test] 안에 WebSecurityConfig.java 파일 생성 후 아래와 같이 작성
package net.datasa.test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
//로그인 없이 접근 가능한 경로
private static final String[] PUBLIC_URLS = {
"/" //메인화면
, "/view1" //로그인 없이 접근할 수 있는 페이지
, "/images/**"
, "/css/**"
, "/js/**"
};
// Bean : 메모리에 만들어진 객체(즉, 객체를 생성해서 메모리에 로드시켜 놓는 역할을 한다!)
@Bean
protected SecurityFilterChain config(HttpSecurity http) throws Exception {
http
//요청에 대한 권한 설정
.authorizeHttpRequests(author -> author
.requestMatchers(PUBLIC_URLS).permitAll() //모두 접근 허용
// 모든 요청은 인증이 필요하다는 코드이며, 바로 위의 코드만 인증 없이 모두 접근 가능하다!
.anyRequest().authenticated() //그 외의 모든 요청은 인증 필요
)
//HTTP Basic 인증을 사용하도록 설정
.httpBasic(Customizer.withDefaults())
//폼 로그인 설정
.formLogin(formLogin -> formLogin
.loginPage("/loginForm") //로그인폼 페이지 경로
.usernameParameter("id") //폼의 ID 파라미터 이름
.passwordParameter("password") //폼의 비밀번호 파라미터 이름
.loginProcessingUrl("/login") //로그인폼 제출하여 처리할 경로
.defaultSuccessUrl("/") //로그인 성공 시 이동할 경로
.permitAll() //로그인 페이지는 모두 접근 허용
)
//로그아웃 설정
.logout(logout -> logout
.logoutUrl("/logout") //로그아웃 처리 경로
.logoutSuccessUrl("/") //로그아웃 성공 시 이동할 경로
);
http
.cors(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
//비밀번호 암호화를 위한 인코더를 빈으로 등록
@Bean
public BCryptPasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
(6) [src/main/resources] - [templates] 안에 loginForm.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
</head>
<body>
<h1>[ 로그인 ]</h1>
<form action="login" method="post">
ID <input type="text" name="id"><br>
PW <input type="password" name="password"><br>
<input type="submit" value="Login"><br>
</form>
</body>
</html>
(7) [src/main/java] - [net.datasa.test] 안에 AuthenticatedUser.java 파일 생성 후 아래와 같이 작성
package net.datasa.test;
import java.util.Collection;
import java.util.Collections;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AuthenticatedUser implements UserDetails {
// 객체 직렬화
private static final long serialVersionUID = 1562050567301951305L;
String id;
String password;
String roleName;
boolean enabled;
String name;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// "ROLE_USER", "ROLE_ADMIN"
return Collections.singletonList(new SimpleGrantedAuthority(roleName));
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return id;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
(8) [src/main/java] - [net.datasa.test] 안에 AuthenticatedUserDetailsService.java 파일 생성 후 아래와 같이 작성
package net.datasa.test;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthenticatedUserDetailsService implements UserDetailsService {
private final BCryptPasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 전달받은 아이디로 회원정보 DB에서 조회
// 없으면 예외
// 있으면 그 정보로 UserDetails 객체 생성하여 리턴함
AuthenticatedUser user = AuthenticatedUser.builder()
.id("abc")
.password(passwordEncoder.encode("123"))
.name("홍길동")
.roleName("ROLE_USER")
.enabled(true)
.build();
log.debug("인증정보: {}", user);
return user;
}
}
(9) 결과 화면
"로그인" 전 화면 : "view1", "로그인" 접근 가능 / 로그인 시 ID : abc, password : 123
"로그인" 후 화면 : "view1", " view2", "로그아웃" 접근 가능 / 로그아웃 시 위의 화면으로 돌아감
'SpringBoot' 카테고리의 다른 글
SpringBoot(21) - 복습 예제(회원가입 기능 구현) (0) | 2024.07.24 |
---|---|
SpringBoot(20) - Security 예제(Thymeleaf), 복습 예제(회원가입) (0) | 2024.07.23 |
SpringBoot(18) - Server, JPA, DB 예제(글 목록, 글 삭제, 글 수정) (0) | 2024.07.18 |
SpringBoot(17) - Server, JPA, DB 예제(테스트 저장, 방명록 쓰기) (0) | 2024.07.17 |
SpringBoot(16) - Server, JPA, DB 예제(각 아이디별 회원 정보 수정), 추가 정리 사항 (0) | 2024.07.12 |