Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#12 security #13

Merged
merged 4 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ dependencies {

//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'

//security
implementation "org.springframework.boot:spring-boot-starter-security"
implementation "io.jsonwebtoken:jjwt-api:0.11.5"
implementation "io.jsonwebtoken:jjwt-impl:0.11.5"
implementation "io.jsonwebtoken:jjwt-jackson:0.11.5"
}

tasks.named('test') {
Expand Down
98 changes: 98 additions & 0 deletions src/main/java/com/backend/soullive_a/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.backend.soullive_a.config;


import static org.springframework.http.HttpMethod.POST;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

import com.backend.soullive_a.security.filter.JwtAuthenticationFilter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

//RequestMatcher[] requestMatchers = requestMatchersPermitAll();
//setHttpSecurity(http);
http
.securityMatchers(matcher -> matcher.requestMatchers("/**"))
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize ->
authorize.requestMatchers("/**").permitAll()
.anyRequest().denyAll());

http
.addFilterBefore(new JwtAuthenticationFilter()
, UsernamePasswordAuthenticationFilter.class);

return http.build();
}

private RequestMatcher[] requestMatchersPermitAll() {
List<RequestMatcher> requestMatchers = List.of(
antMatcher(POST, "/**")
);
return requestMatchers.toArray(RequestMatcher[]::new);
}

private void setHttpSecurity(HttpSecurity http)
throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable);
}

@Bean
public static PasswordEncoder passwordEncoder() {

String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());

return new DelegatingPasswordEncoder(idForEncode, encoders);
}


@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();

configuration.addAllowedOriginPattern("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.backend.soullive_a.controller;

import com.backend.soullive_a.entity.User;
import com.backend.soullive_a.exception.base.BaseResponse;
import com.backend.soullive_a.exception.custom.NotFoundUserException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class ExceptionController {
@GetMapping("/success")
public BaseResponse<Object> getUser() {
Expand All @@ -21,4 +25,12 @@ public BaseResponse<Object> getUser() {
public BaseResponse<Object> notFoundUser() {
throw new NotFoundUserException();
}
/*
* @AuthenticationPrincipal ์‚ฌ์šฉ๋ฒ• ์˜ˆ์‹œ
*/
@GetMapping("/test")
public String test(@AuthenticationPrincipal User user){
log.info("user ์•„์ด๋”” ๊ฐ’: {}",user.getId());
return "test";
}
}
30 changes: 30 additions & 0 deletions src/main/java/com/backend/soullive_a/entity/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.backend.soullive_a.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "user")
@Getter
@Builder @NoArgsConstructor @AllArgsConstructor
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;

private String phoneNumber;

private String password;

//TODO: ์—ญํ•  ์ถ”๊ฐ€
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.backend.soullive_a.repository;

import com.backend.soullive_a.entity.User;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByPhoneNumber(String phoneNumber);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.backend.soullive_a.security.dto.response;

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

@Data
@Builder
@AllArgsConstructor @NoArgsConstructor
public class TokenInfoDto {

private String accessToken;

private String refreshToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.backend.soullive_a.security.filter;


import com.backend.soullive_a.entity.User;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.NoArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

@NoArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final String AUTHENTICATION_HEADER = "Authorization";
private final String AUTHENTICATION_SCHEME = "Bearer ";

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {

SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(getAuthentication());
SecurityContextHolder.setContext(context);

filterChain.doFilter(request, response);
}

private String extractToken(HttpServletRequest request) {
String bearToken = request.getHeader(AUTHENTICATION_HEADER);

if (StringUtils.hasText(bearToken) && bearToken.startsWith(AUTHENTICATION_SCHEME)) {
return bearToken.substring(AUTHENTICATION_SCHEME.length());
}

return null;

}

private Authentication getAuthentication() {

User user = User.builder()
.id(1L)
.phoneNumber("010-3370-2740")
.password("1234")
.build();

return new UsernamePasswordAuthenticationToken(user, null, null);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.backend.soullive_a.security.service;
/*
import com.backend.soullive_a.entity.User;
import java.util.Collection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserDetailsImpl implements UserDetails {

private Long id;

private String phoneNumber;

private String password;

public static UserDetails fromEntity(User user) {

return UserDetailsImpl.builder()
.id(user.getId())
.phoneNumber(user.getPhoneNumber())
.password(user.getPassword())
.build();
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}

@Override
public String getPassword() {
return this.password;
}

@Override
public String getUsername() {
return this.phoneNumber;
}

@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,28 @@
package com.backend.soullive_a.security.service;

/*
import com.backend.soullive_a.entity.User;
import com.backend.soullive_a.exception.custom.NotFoundUserException;
import com.backend.soullive_a.repository.UserRepository;
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
public class UserDetailsServiceImpl implements UserDetailsService {

private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String phoneNumber) throws UsernameNotFoundException {

User user = userRepository.findByPhoneNumber(phoneNumber)
.orElseThrow(NotFoundUserException::new);

return UserDetailsImpl.fromEntity(user);
}
}
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.backend.soullive_a.security.utils;
/*
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class JwtProvider {

@Value("${jwt.secret-key}")
private String secretKey;

@Value("${jwt.access-expiration}")
private int accessExpirationSeconds;


public String createAccessToken(Authentication authentication) {

return null;
}
}
*/
Loading