Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
bcfa152
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
2320382
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
a4fbd09
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
619f926
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
bea7634
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
72af7b4
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
83ea55f
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
136a3da
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
f865151
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
2e57c1e
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
a75c279
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
a1e8078
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
153fd3e
프로젝트 생성 및 기본구성
onjsdnjs Jan 28, 2024
a536d87
프로젝트 생성 및 기본구성
onjsdnjs Jan 29, 2024
a401ddc
프로젝트 생성 및 기본구성
onjsdnjs Jan 29, 2024
9eb3f1a
5-CustomUserDetailService_구현하기
onjsdnjs Jan 29, 2024
40b67ea
spring security master
onjsdnjs Jan 29, 2024
47434d5
spring security master
onjsdnjs Jan 29, 2024
3702609
spring security master
onjsdnjs Jan 29, 2024
0cbc5c4
spring security master
onjsdnjs Jan 29, 2024
79d9e7b
spring security master
onjsdnjs Jan 29, 2024
56f9195
spring security master
onjsdnjs Jan 29, 2024
2fff87b
spring security master
onjsdnjs Jan 29, 2024
c3c23f2
spring security master
onjsdnjs Jan 29, 2024
17d9d86
spring security master
onjsdnjs Jan 29, 2024
502a61f
spring security master
onjsdnjs Jan 29, 2024
9fe663c
spring security master
onjsdnjs Jan 29, 2024
0bd4698
spring security master
onjsdnjs Jan 29, 2024
9f0d773
spring security master
onjsdnjs Jan 29, 2024
dbac13d
spring security master
onjsdnjs Jan 29, 2024
a7ef259
spring security master
onjsdnjs Jan 29, 2024
ccc1ce8
spring security master
onjsdnjs Jan 29, 2024
10474a7
spring security master
onjsdnjs Jan 29, 2024
9af9348
spring security master
onjsdnjs Jan 29, 2024
b3f4ebd
spring security master
onjsdnjs Jan 29, 2024
3eb09ee
spring security master
onjsdnjs Jan 29, 2024
76fb1e2
spring security master
onjsdnjs Jan 29, 2024
2211fcf
spring security master
onjsdnjs Jan 29, 2024
dee0759
spring security master
onjsdnjs Jan 29, 2024
8697711
spring security master
onjsdnjs Jan 29, 2024
0a25b49
spring security master
onjsdnjs Jan 29, 2024
07aaf75
spring security master
onjsdnjs Jan 29, 2024
202d0af
spring security master
onjsdnjs Jan 29, 2024
cbe3977
spring security master
onjsdnjs Jan 29, 2024
4a70e6e
spring security master
onjsdnjs Jan 29, 2024
5400850
spring security master
onjsdnjs Jan 29, 2024
53d64c7
spring security master
onjsdnjs Jan 29, 2024
7e7298c
spring security master
onjsdnjs Jan 29, 2024
06fa523
spring security master
onjsdnjs Jan 29, 2024
60e35eb
spring security master
onjsdnjs Jan 29, 2024
f94d507
spring security master
onjsdnjs Jan 29, 2024
31e7ff0
spring security master
onjsdnjs Jan 29, 2024
408bf65
spring security master
onjsdnjs Jan 29, 2024
db37eac
spring security master
onjsdnjs Jan 29, 2024
b04b59c
spring security master
onjsdnjs Jan 30, 2024
10a359c
spring security master
onjsdnjs Jan 30, 2024
19f44b8
spring security master
onjsdnjs Jan 30, 2024
0a18d96
spring security master
onjsdnjs Jan 30, 2024
46da1ca
spring security master
onjsdnjs Jan 30, 2024
f0a0569
spring security master
onjsdnjs Jan 30, 2024
a53314b
spring security master
onjsdnjs Jan 30, 2024
74983b1
spring security master
onjsdnjs Jan 30, 2024
306631c
spring security master
onjsdnjs Jan 30, 2024
332c3a4
spring security master
onjsdnjs Jan 30, 2024
f2c11cc
spring security master
onjsdnjs Jan 30, 2024
adf90f3
spring security master
onjsdnjs Jan 30, 2024
e3f8247
spring security master
onjsdnjs Jan 30, 2024
fd65650
spring security master
onjsdnjs Jan 30, 2024
5d0bae4
spring security master
onjsdnjs Jan 31, 2024
3d9f811
spring security master
onjsdnjs Jan 31, 2024
31e7c2b
spring security master
onjsdnjs Jan 31, 2024
8df2151
spring security master
onjsdnjs Jan 31, 2024
a07d5d9
spring security master
onjsdnjs Jan 31, 2024
6559c8d
spring security master
onjsdnjs Feb 1, 2024
57e7dc5
spring security master
onjsdnjs Feb 1, 2024
a9321a6
spring security master
onjsdnjs Feb 1, 2024
ae29b25
spring security master
onjsdnjs Feb 3, 2024
c4eca89
spring security master
onjsdnjs Feb 3, 2024
e16b8fa
spring security master
onjsdnjs Feb 5, 2024
dd1e29f
spring security master
onjsdnjs Mar 15, 2024
478f390
spring security master
onjsdnjs Mar 15, 2024
5b53ccf
spring security master
onjsdnjs Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ configurations {
repositories {
mavenCentral()
}

//FIXME
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version: '3.2.2'
implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity6', version: '3.1.2.RELEASE'
implementation 'org.modelmapper:modelmapper:3.1.0'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class DashboardController {
public class HomeController {
@GetMapping(value="/")
public String dashboard() {
return "/dashboard";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.security.springsecuritymaster.domain.dto;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

@Data
public class AccountContext implements UserDetails {
private AccountDto accountDto;
private final List<GrantedAuthority> roles;

public AccountContext(AccountDto accountDto, List<GrantedAuthority> roles) {
this.accountDto = accountDto;
this.roles = roles;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@Override
public String getPassword() {
return accountDto.getPassword();
}
@Override
public String getUsername() {
return accountDto.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.security.springsecuritymaster.domain.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountDto {
private String id;
private String username;
private String password;
private int age;
private String roles;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.security.springsecuritymaster.domain.entity;

import jakarta.persistence.*;
import lombok.Data;
import java.io.Serializable;

@Entity
@Data
public class Account implements Serializable {

@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private String roles;
private int age;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.security.springsecuritymaster.security.configs;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class AuthConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

}
Original file line number Diff line number Diff line change
@@ -1,34 +1,90 @@
package io.security.springsecuritymaster.security.configs;

import io.security.springsecuritymaster.security.filters.RestAuthenticationFilter;
import io.security.springsecuritymaster.security.handler.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

private final AuthenticationProvider authenticationProvider;
private final AuthenticationProvider restAuthenticationProvider;
private final AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource;
private final FormAuthenticationSuccessHandler successHandler;
private final FormAuthenticationFailureHandler failureHandler;
private final RestAuthenticationSuccessHandler restSuccessHandler;
private final RestAuthenticationFailureHandler restFailureHandler;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/").permitAll()
.requestMatchers("/css/**", "/images/**", "/js/**", "/favicon.*", "/*/icon-*").permitAll()
.requestMatchers("/","/signup","/login*").permitAll()
.requestMatchers("/user").hasAuthority("ROLE_USER")
.requestMatchers("/manager").hasAuthority("ROLE_MANAGER")
.requestMatchers("/admin").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())

.formLogin(form -> form
.loginPage("/login")
.authenticationDetailsSource(authenticationDetailsSource)
.successHandler(successHandler)
.failureHandler(failureHandler)
.permitAll())
// .csrf(AbstractHttpConfigurer::disable)
.authenticationProvider(authenticationProvider)
.exceptionHandling(exception -> exception
.accessDeniedHandler(new FormAccessDeniedHandler("/denied"))
)
;
return http.build();
}


@Bean
public UserDetailsService userDetailsService(){
UserDetails user = User.withUsername("user").password("{noop}1111").roles("USER").build();
return new InMemoryUserDetailsManager(user);
@Order(1)
public SecurityFilterChain restSecurityFilterChain(HttpSecurity http) throws Exception {

AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.authenticationProvider(restAuthenticationProvider);
AuthenticationManager authenticationManager = authenticationManagerBuilder.build(); // build() 는 최초 한번 만 호출해야 한다

http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/css/**", "/images/**", "/js/**", "/favicon.*", "/*/icon-*").permitAll()
.anyRequest().permitAll())
.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(restAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
.authenticationManager(authenticationManager)
;
return http.build();
}

private RestAuthenticationFilter restAuthenticationFilter(AuthenticationManager authenticationManager) {

RestAuthenticationFilter restAuthenticationFilter = new RestAuthenticationFilter();
restAuthenticationFilter.setAuthenticationManager(authenticationManager);
restAuthenticationFilter.setAuthenticationSuccessHandler(restSuccessHandler);
restAuthenticationFilter.setAuthenticationFailureHandler(restFailureHandler);

return restAuthenticationFilter;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.security.springsecuritymaster.security.details;

import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

@Getter
public class FormWebAuthenticationDetails extends WebAuthenticationDetails {

private final String secretKey;

public FormWebAuthenticationDetails(HttpServletRequest request) {
super(request);
secretKey = request.getParameter("secret_key");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.security.springsecuritymaster.security.details;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;

@Component
public class FormWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest request) {
return new FormWebAuthenticationDetails(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.security.springsecuritymaster.security.exception;

import org.springframework.security.core.AuthenticationException;

public class SecretException extends AuthenticationException {
public SecretException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.security.springsecuritymaster.security.filters;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.security.springsecuritymaster.domain.dto.AccountDto;
import io.security.springsecuritymaster.security.token.RestAuthenticationToken;
import io.security.springsecuritymaster.util.WebUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import java.io.IOException;

public class RestAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final ObjectMapper objectMapper = new ObjectMapper();
public RestAuthenticationFilter() {
super(new AntPathRequestMatcher("/api/login", "POST"));
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {

if (!HttpMethod.POST.name().equals(request.getMethod()) || !WebUtil.isAjax(request)) {
throw new IllegalArgumentException("Authentication method not supported");
}

AccountDto accountDto = objectMapper.readValue(request.getReader(), AccountDto.class);

if (!StringUtils.hasText(accountDto.getUsername()) || !StringUtils.hasText(accountDto.getPassword())) {
throw new AuthenticationServiceException("Username or Password not provided");
}
RestAuthenticationToken token = new RestAuthenticationToken(accountDto.getUsername(),accountDto.getPassword());

return this.getAuthenticationManager().authenticate(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.security.springsecuritymaster.security.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;


public class FormAccessDeniedHandler implements AccessDeniedHandler {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private final String errorPage;

public FormAccessDeniedHandler(String errorPage) {
this.errorPage = errorPage;
}

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {

String deniedUrl = errorPage + "?exception=" + accessDeniedException.getMessage();
redirectStrategy.sendRedirect(request, response, deniedUrl);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.security.springsecuritymaster.security.handler;

import io.security.springsecuritymaster.security.exception.SecretException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component("failureHandler")
public class FormAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

@Override
public void onAuthenticationFailure(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException exception) throws IOException, ServletException {

String errorMessage = "Invalid Username or Password";

if(exception instanceof BadCredentialsException) {
errorMessage = "Invalid Username or Password";
}
else if(exception instanceof UsernameNotFoundException) {
errorMessage = "User not exists";
}
else if(exception instanceof CredentialsExpiredException) {
errorMessage = "Expired password";

}else if(exception instanceof SecretException) {
errorMessage = "Invalid Secret key";
}

setDefaultFailureUrl("/login?error=true&exception=" + errorMessage);

super.onAuthenticationFailure(request, response, exception);

}
}
Loading