๋ชฉ์ฐจ
( ์ ๊ฐ ์์ ํ๋ ํ๋ก์ ํธ ์ฝ๋ ๊ธฐ๋ฐ์ผ๋ก ์ค๋ช ํ๊ณ ์์ต๋๋ค.)
1. build gradle ์ ์์กด์ฑ ์ถ๊ฐ
//์ํ๋ฆฌํฐ ๋ฒ์ ์ ์คํ๋ง๋ฒ์ ์ ๋ฐ๋ผ ์ฌ์ฉ๋ฐฉ๋ฒ์ด ์์ ํ ๋ค๋ฆ
๋๋ค.
//์ํ๋ฆฌํฐ 5๋ฒ์ => ์คํ๋ง ๋ถํธ 2๋ฒ์ (์์
)
//์ํ๋ฆฌํฐ 6๋ฒ์ => ์คํ๋ง ๋ถํธ 3๋ฒ์ (๋ฌธ๋ฒ์ด ์์ ๋ณ๊ฒฝ๋๋ ์ฃผ์)
implementation 'org.springframework.boot:spring-boot-starter-security'
//์ํ๋ฆฌํฐ ํ์๋ฆฌํ์์ ์ฌ์ฉ
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
//์ํ๋ฆฌํฐ ํ
์คํธ
testImplementation 'org.springframework.security:spring-security-test'
- ์ํ๋ฆฌํฐ๊ฐ ์ฒ์ ์ค์ ๋๋ฉด ๋ชจ๋ ์์ฒญ์ ๋ํด์ ์ํ๋ฆฌ๊ฐ ๊ธฐ๋ณธ ์ ๊ณตํ๋ ๋ก๊ทธ์ธ ํ๋ฉด์ด ๋ณด์ด๊ฒ ๋จ.
- ๊ธฐ๋ณธ์์ด๋๋ user / ๋น๋ฐ๋ฒํธ๋ console์ฐฝ์ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฐ๊ธ๋จ.
- ๋ก๊ทธ์์์ ๊ฒฝ๋ก๋ /logout์ด ๊ธฐ๋ณธ์ด ๋๋ค.
2. ์ํ๋ฆฌํฐ ์ค์ ํ์ผ ๊ตฌํ
security_config.java
์ฃผ์๋ฉ์๋
3. SecurityConfig ์ SecurityFilterChain ๊ตฌํ
package com.project.tobe.config;
import com.project.tobe.dto.UserRole;
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.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity //์ํ๋ฆฌํฐ ์ค์ ํ์ผ์ ์ํ๋ฆฌํฐ ํํฐ์ ๋ฑ๋ก
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder(); // ๋น๋ฐ๋ฒํธ ์ํธํ๋ฅผ ์ํ ์ธ์ฝ๋
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()//csrfํ ํฐ ์ฌ์ฉx
.authorizeRequests() // ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ ๊ท์น ์ค์
.antMatchers("/login.user").permitAll() // ๋ก๊ทธ์ธ ํ์ด์ง๋ ์ธ์ฆ ์์ด ์ ๊ทผ ํ์ฉ
.antMatchers("/employee.do").hasAnyRole("S", "A") // "/employee.do" ๊ฒฝ๋ก๋ "A","S" ์ญํ ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅ
.antMatchers("/*.do").authenticated() // ".do"๋ก ๋๋๋ ๋ชจ๋ ์์ฒญ์ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅ
.anyRequest().permitAll() // ๊ทธ์ธ ์์ฒญ์ ์ธ์ฆ ์์ด ์ ๊ทผ ๊ฐ๋ฅ
.and()
.formLogin() // ๋ก๊ทธ์ธ ์ค์
.loginPage("/login.user") // ๋ก๊ทธ์ธ ํ์ด์ง ์ค์ (์ฌ์ฉ์ ์ ์)
.usernameParameter("employeeId") //๋ก๊ทธ์ธํผ์์ ์์ด๋ํ๋ name ๊ฐ์ employeeId ๋ก ์ค์
.passwordParameter("employeePw") //๋ก๊ทธ์ธํผ์์ ๋น๋ฐ๋ฒํธํ๋ name ๊ฐ์ employeePw ๋ก ์ค์
.loginProcessingUrl("/loginForm") //๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๊ฐ๋ก์ฑ ์ํ๋ฆฌํฐ๊ฐ ์ ๊ณตํ๋ ํด๋์ค๋ก ๋ก๊ทธ์ธ์ ์ฐ๊ฒฐ
.defaultSuccessUrl("/main.do") //๋ก๊ทธ์ธ ์ฑ๊ณต ์ ์ด๋ํ URL
.failureUrl("/login.user?err=true") // ๋ก๊ทธ์ธ ์คํจ์ ์ด๋ URL
// .failureHandler(new CustomAuthenticationFailureHandler()) // ์ปค์คํ
์คํจ ํธ๋ค๋ฌ ๋ฑ๋ก
.and()
.logout() // ๋ก๊ทธ์์ ์ค์
.logoutUrl("/logout") // ๋ก๊ทธ์์ ํ์ด์ง ์ค์
.logoutSuccessUrl("/login.user") // ๋ก๊ทธ์์ ์ฑ๊ณต ์ ์ด๋ํ URL
.invalidateHttpSession(true)// ๋ก๊ทธ์์ ์ ์ธ์
๋ฌดํจํ
.deleteCookies("JSESSIONID") // ๋ก๊ทธ์์ ์ ์ฟ ํค ์ญ์ (JSESSIONID)
.and()
.exceptionHandling() //์ธ์ฆ(Authentication) ๋๋ ๊ถํ ๋ถ์ฌ(Authorization) ๊ด๋ จ ์์ธ ์ฒ๋ฆฌ
.accessDeniedPage("/accessDenied"); //์ ๊ทผ๊ถํ์ด ์๋ ์์ฒญ์ accessDenied ํ์ด์ง๋ก ์ด๋
return http.build();
}
}
ํด๋น ํํ๋ฅผ ์ํ๋ฆฌํฐ์๊ฒ ์ ๋ฌํด์ผํ๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด ๊ตฌํ์ ๋จผ์ ํด์ค์ผํ๋ค.
4. UserDetailsService ๊ตฌํ (์ธ์ฆ๊ฐ์ฒด ๋ง๋ค๊ธฐ)
package com.project.tobe.serviceimpl;
import com.project.tobe.entity.Employee;
import com.project.tobe.repository.EmployeeRepository;
//import com.project.tobe.security.EmployeeDetails;
import com.project.tobe.security.EmployeeDetails;
import lombok.RequiredArgsConstructor;
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;
@Service
@RequiredArgsConstructor
//loginProcessingUrl()๊ฐ ํธ์ถ๋๋ฉด loadUserByUsername์ด ์๋ ํธ์ถ๋จ
public class PrincipalDetailsService implements UserDetailsService {
private final EmployeeRepository employeeRepository;
/**
* ์ฌ์ฉ์์ ์์ด๋(username)๋ฅผ ํตํด ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ก๋ํ๋ ๋ฉ์๋์
๋๋ค.
* ์คํ๋ง ์ํ๋ฆฌํฐ์์ ๋ก๊ทธ์ธ ์์ฒญ ์ ํธ์ถ๋ฉ๋๋ค.
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ username์ ๊ธฐ์ค์ผ๋ก Employee ๊ฐ์ฒด ์กฐํ
Employee employee = employeeRepository.findById(username)
.orElseThrow(() -> {
// username ์ ํด๋นํ๋ ์ฌ์ฉ์๊ฐ ์์ผ๋ฉด ์์ธ ๋ฐ์
return new UsernameNotFoundException("ํด๋น ์ ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
});
// ์กฐํ๋ ์ฌ์ฉ์ ๊ฐ์ฒด๋ฅผ EmployeeDetails (์ ์ ๊ฐ์ฒด) ๋ก ๋ณํํ์ฌ ๋ฐํ
// ์ํ๋ฆฌํฐ์ธ์
( new Authentication(new MyUserDetails()) ) ํ์์ผ๋ก ์ ๋ฌ ๋จ
return new EmployeeDetails(employee);
}
}
5.UserDetails ๊ตฌํ (์ ์ ๊ฐ์ฒด ๋ง๋ค๊ธฐ)
package com.project.tobe.security;
import com.project.tobe.dto.EmployeeDTO;
import com.project.tobe.entity.Employee;
import com.project.tobe.dto.UserRole;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class EmployeeDetails implements UserDetails {
private Employee employee;
private EmployeeDTO employeedto;
public EmployeeDetails(Employee employee) {
this.employee = employee;
}
// ๊ถํ ๊ด๋ จ ์์
์ ํ๊ธฐ ์ํ role return
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
try {
UserRole role = UserRole.valueOf(employee.getAuthorityGrade()); // authorityGrade๋ฅผ UserRole Enum์ผ๋ก ๋ณํ
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.name())); // ROLE_ ์ ๋์ฌ ๋ถ์ด๊ธฐ
} catch (IllegalArgumentException e) {
// authorityGrade ๊ฐ์ด UserRole Enum์ ์ ์๋์ด ์์ง ์์ ๊ฒฝ์ฐ์ ์ฒ๋ฆฌ
System.err.println("Invalid authorityGrade value: " + employee.getAuthorityGrade());
}
return authorities;
}
// get Password ๋ฉ์๋
@Override
public String getPassword() {
return employee.getEmployeePw();
}
// get Username ๋ฉ์๋ (์์ฑํ User์ loginId ์ฌ์ฉ)
@Override
public String getUsername() {
return employee.getEmployeeId();
}
public String getNickname() {
System.out.println(employee.toString());
System.out.println(employee.getEmployeeName());
return employee.getEmployeeName();
}
public String getUserAuthorityGrade() {
return employee.getAuthorityGrade();
}
// public String getUserAuthorityName() {
// System.out.println(employeedto.getAuthorityName());
// return employeedto.getAuthorityName();
// }
// ๊ณ์ ์ด ๋ง๋ฃ ๋์๋์ง (true: ๋ง๋ฃX)
@Override
public boolean isAccountNonExpired() {
return true;
}
// ๊ณ์ ์ด ์ ๊ฒผ๋์ง (true: ์ ๊ธฐ์ง ์์)
@Override
public boolean isAccountNonLocked() {
return true;
}
// ๋น๋ฐ๋ฒํธ๊ฐ ๋ง๋ฃ๋์๋์ง (true: ๋ง๋ฃX)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// ๊ณ์ ์ด ํ์ฑํ(์ฌ์ฉ๊ฐ๋ฅ)์ธ์ง (true: ํ์ฑํ)
@Override
public boolean isEnabled() {
return true;
}
}
6. ์ธ์ฆ ๊ฐ์ฒด ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
์ปจํธ๋กค๋ฌ
//ํ์ฌ ๋ก๊ทธ์ธ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
@GetMapping("/user-info")
public ResponseEntity<?> employeeUserInfo(Authentication authentication) {
if (authentication == null) { // ์ธ์ฆ ๊ฐ์ฒด๊ฐ ์๋ค๋ฉด
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
}
//์ธ์ฆ๊ฐ์ฒด ์์ principal๊ฐ์ ์ป์ผ๋ฉด ์ ์ ๊ฐ์ฒด๊ฐ ๋์ด
EmployeeDetails user = (EmployeeDetails)authentication.getPrincipal();
System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (์ด๋ฆ):" + user.getNickname());
System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (์์ด๋):" + user.getUsername());
System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (๋น๋ฐ๋ฒํธ) :" + user.getPassword());
System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (๊ถํ) :" + user.getUserAuthorityGrade());
// ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๊ถํ์ ๋ฐํ
Map<String, Object> response = new HashMap<>();
response.put("userName", user.getNickname());
response.put("userId", user.getUsername());
response.put("userPw", user.getPassword());
response.put("userAuthorityGrade", user.getUserAuthorityGrade());
return ResponseEntity.ok(response);
}
view (ํ์๋ฆฌํ)
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
.
.
.
<!-- ์ธ์ฆ ๋์๋ค๋ฉด true, ์๋๋ฉด false๋ฅผ ๋ฐํํฉ๋๋ค. if๋ฌธ์ฒ๋ผ ์ฌ์ฉ๋ฉ๋๋ค. -->
<div sec:authorize="isAuthenticated()">
<span>[[${#authentication.principal.getUsername}]]</span><br/>
<span>[[${#authentication.principal.getNickname}]]</span><br/>
<span>[[${#authentication.principal.getUserAuthorityGrade}]]</span><br/>
</div>
view (๋ฆฌ์กํธ)
useEffect(() => {
const fetchUserId = async () => {
try {
const response = await axios.get('/employee/user-info');
console.log("์ธ์ฆ๋ ์ฌ์ฉ์ ์ ๋ณด" + response.data);
} catch (error) {
console.error("Error fetching user ID:", error);
}
};
fetchUserId();
}, []);
7. ์ธ์ - ์ฟ ํค ๋ฐฉ์