Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 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
80971c1
spring security master
onjsdnjs Jan 30, 2024
845c6d1
spring security master
onjsdnjs Jan 30, 2024
3b578c8
spring security master
onjsdnjs Jan 30, 2024
01c31f1
spring security master
onjsdnjs Jan 30, 2024
117f93d
spring security master
onjsdnjs Jan 30, 2024
257f942
spring security master
onjsdnjs Jan 30, 2024
4dd2f19
spring security master
onjsdnjs Jan 30, 2024
2895a31
spring security master
onjsdnjs Jan 30, 2024
e401a01
spring security master
onjsdnjs Jan 30, 2024
91a9fd0
spring security master
onjsdnjs Jan 30, 2024
3bf0017
spring security master
onjsdnjs Jan 30, 2024
9226a61
spring security master
onjsdnjs Jan 30, 2024
79b89ef
spring security master
onjsdnjs Jan 30, 2024
2dc73f9
spring security master
onjsdnjs Jan 30, 2024
e7487ba
spring security master
onjsdnjs Jan 30, 2024
098af6d
spring security master
onjsdnjs Jan 30, 2024
eac9db0
spring security master
onjsdnjs Jan 30, 2024
7756bfd
spring security master
onjsdnjs Jan 30, 2024
e08bc1c
spring security master
onjsdnjs Jan 30, 2024
a7bccd5
spring security master
onjsdnjs Jan 30, 2024
0a49f02
spring security master
onjsdnjs Jan 30, 2024
786d2b7
spring security master
onjsdnjs Jan 30, 2024
edbee0b
spring security master
onjsdnjs Jan 30, 2024
9dae6ed
spring security master
onjsdnjs Jan 30, 2024
e22296d
spring security master
onjsdnjs Jan 30, 2024
bb5e302
spring security master
onjsdnjs Jan 30, 2024
f41aa4a
spring security master
onjsdnjs Jan 30, 2024
eac6302
spring security master
onjsdnjs Jan 30, 2024
9151009
spring security master
onjsdnjs Jan 30, 2024
fa483b2
spring security master
onjsdnjs Jan 30, 2024
64cbf66
spring security master
onjsdnjs Jan 30, 2024
f3e26b2
spring security master
onjsdnjs Jan 30, 2024
8064f8a
spring security master
onjsdnjs Jan 30, 2024
79c681d
spring security master
onjsdnjs Jan 30, 2024
8985f1e
spring security master
onjsdnjs Jan 31, 2024
3e911a9
spring security master
onjsdnjs Jan 31, 2024
8df9f95
spring security master
onjsdnjs Jan 31, 2024
73ca9c3
spring security master
onjsdnjs Jan 31, 2024
54d4627
spring security master
onjsdnjs Jan 31, 2024
3df4561
spring security master
onjsdnjs Jan 31, 2024
ddbfe3d
spring security master
onjsdnjs Feb 1, 2024
7788570
spring security master
onjsdnjs Feb 1, 2024
2f156f5
spring security master
onjsdnjs Feb 1, 2024
29bb975
spring security master
onjsdnjs Feb 3, 2024
877068d
spring security master
onjsdnjs Feb 3, 2024
c471194
spring security master
onjsdnjs Feb 4, 2024
db26be2
spring security master
onjsdnjs Feb 4, 2024
4bf6741
spring security master
onjsdnjs Mar 15, 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 All @@ -25,4 +25,9 @@ public String manager() {
public String admin() {
return "/admin";
}

@GetMapping(value="/api")
public String restDashboard() {
return "rest/dashboard";
}
}
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,98 @@
package io.security.springsecuritymaster.security.configs;

import io.security.springsecuritymaster.security.entrypoint.RestAuthenticationEntryPoint;
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())
.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()
.requestMatchers("/api","/api/login").permitAll()
.requestMatchers("/api/user").hasAuthority("ROLE_USER")
.requestMatchers("/api/manager").hasAuthority("ROLE_MANAGER")
.requestMatchers("/api/admin").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(restAuthenticationFilter(http, authenticationManager), UsernamePasswordAuthenticationFilter.class)
.authenticationManager(authenticationManager)
.exceptionHandling(exception -> exception
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.accessDeniedHandler(new RestAccessDeniedHandler())
)
;
return http.build();
}

private RestAuthenticationFilter restAuthenticationFilter(HttpSecurity http, AuthenticationManager authenticationManager) {

RestAuthenticationFilter restAuthenticationFilter = new RestAuthenticationFilter(http);
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,24 @@
package io.security.springsecuritymaster.security.entrypoint;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import java.io.IOException;

public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final ObjectMapper mapper = new ObjectMapper();

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write(mapper.writeValueAsString(HttpServletResponse.SC_UNAUTHORIZED));
}
}
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 msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
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(HttpSecurity http) {
super(new AntPathRequestMatcher("/api/login", "POST"));
setSecurityContextRepository(getSecurityContextRepository(http));
}

private SecurityContextRepository getSecurityContextRepository(HttpSecurity http) {
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
if (securityContextRepository == null) {
securityContextRepository = new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository());
}
return securityContextRepository;
}

@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);
}
}
Loading