package com.speech.up.auth.provider;
import org.springframework.security.oauth2.core.user.OAuth2User;
import com.speech.up.auth.service.implement.UserAuthorizationType;
import com.speech.up.auth.service.servicetype.LevelType;
import com.speech.up.auth.service.servicetype.ProviderType;
import com.speech.up.user.entity.UserEntity;
public class GithubProvider implements ProviderOAuth {
final String socialId;
final String email;
final String name;
final String providerType;
final String authorization;
final String level;
final boolean isUse;
public GithubProvider(OAuth2User user) {
this.socialId = user.getAttributes().get("id") + "";
this.name = user.getAttributes().get("name") + "";
this.email = "none";
this.providerType = ProviderType.GITHUB.name();
this.authorization = UserAuthorizationType.ROLE_GENERAL_USER.name();
this.level = LevelType.BRONZE.name();
this.isUse = true;
}
@Override
public UserEntity getUser() {
return UserEntity.providerOf(socialId, email, level, name, authorization, providerType);
}
}
package com.speech.up.auth.provider;
import org.springframework.security.oauth2.core.user.OAuth2User;
import com.speech.up.auth.service.implement.UserAuthorizationType;
import com.speech.up.auth.service.servicetype.LevelType;
import com.speech.up.auth.service.servicetype.ProviderType;
import com.speech.up.user.entity.UserEntity;
public class GoogleProvider implements ProviderOAuth {
final String socialId;
final String email;
final String name;
final String providerType;
final String authorization;
final String level;
final boolean isUse;
public GoogleProvider(OAuth2User user) {
this.socialId = user.getAttributes().get("sub") + "";
this.providerType = ProviderType.GOOGLE.name();
this.email = user.getAttributes().get("email") + "";
this.name = user.getAttributes().get("name") + "";
this.authorization = UserAuthorizationType.ROLE_GENERAL_USER.name();
this.level = LevelType.BRONZE.name();
this.isUse = true;
}
@Override
public UserEntity getUser() {
return UserEntity.providerOf(socialId, email, level, name, authorization, providerType);
}
}
package com.speech.up.auth.provider;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.oauth2.core.user.OAuth2User;
import com.speech.up.auth.service.implement.UserAuthorizationType;
import com.speech.up.auth.service.servicetype.LevelType;
import com.speech.up.auth.service.servicetype.ProviderType;
import com.speech.up.user.entity.UserEntity;
public class KakaoProvider implements ProviderOAuth {
final String socialId;
final String email;
final String name;
final String providerType;
final String authorization;
final String level;
final boolean isUse;
public KakaoProvider(OAuth2User user) {
Object properties = user.getAttributes().get("properties");
Map<String, String> responseMap = new HashMap<>();
if (properties instanceof Map<?, ?> tempMap) {
response(responseMap, tempMap);
}
this.socialId = user.getAttributes().get("id") + "";
this.providerType = ProviderType.KAKAO.name();
this.email = "none";
this.name = responseMap.get("nickname");
this.authorization = UserAuthorizationType.ROLE_GENERAL_USER.name();
this.level = LevelType.BRONZE.name();
this.isUse = true;
}
private void response(Map<String, String> response, Map<?, ?> tempMap) {
for (Map.Entry<?, ?> entry : tempMap.entrySet()) {
if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
response.put((String)entry.getKey(), (String)entry.getValue());
}
}
}
@Override
public UserEntity getUser() {
return UserEntity.providerOf(socialId, email, level, name, authorization, providerType);
}
}
package com.speech.up.auth.service.implement;
import java.util.NoSuchElementException;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import com.speech.up.auth.entity.CustomOAuth2User;
import com.speech.up.auth.provider.Provider;
import com.speech.up.auth.service.servicetype.ProviderType;
import com.speech.up.user.entity.UserEntity;
import com.speech.up.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
public class OAuth2UserServiceImplement extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest request) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(request);
String oauthClientName = request.getClientRegistration().getClientName();
Provider provider = new Provider(oAuth2User);
UserEntity userEntity = provider.getUser(ProviderType.valueOf(oauthClientName.toUpperCase()));
assert userEntity != null;
if (!userRepository.existsBySocialId(userEntity.getSocialId())) {
userRepository.save(userEntity);
} else {
UserEntity user = userRepository.findBySocialId(userEntity.getSocialId())
.orElseThrow(
() -> new NoSuchElementException("not found UserEntity by socialId: " + userEntity.getSocialId()));
UserEntity updateUserAccess = UserEntity.updateUserAccess(user);
userRepository.save(updateUserAccess);
}
return new CustomOAuth2User(userEntity.getSocialId());
}
}
package com.speech.up.user.entity;
import java.time.LocalDateTime;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.speech.up.script.entity.ScriptEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Null;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ToString
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "user")
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
private String name;
@Column(name = "social_id")
private String socialId;
private String email;
private String level;
private String authorization;
private String providerType;
private LocalDateTime lastAccessedAt;
private boolean isUse;
@Null
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
@JsonManagedReference
private List<ScriptEntity> scriptEntity;
private UserEntity(String socialId, String email, String level,
String name, String authorization, String providerType) {
this.socialId = socialId;
this.email = email;
this.level = level;
this.name = name;
this.authorization = authorization;
this.providerType = providerType;
this.lastAccessedAt = LocalDateTime.now();
this.isUse = true;
}
private UserEntity(UserEntity user) {
this.userId = user.getUserId();
this.name = user.getName();
this.socialId = user.getSocialId();
this.email = user.getEmail();
this.level = user.getLevel();
this.providerType = user.getProviderType();
this.authorization = user.getAuthorization();
this.lastAccessedAt = LocalDateTime.now();
this.isUse = true;
}
public static UserEntity providerOf(String socialId, String email, String level,
String name, String authorization, String providerType) {
return new UserEntity(socialId, email, level, name, authorization, providerType);
}
public static UserEntity updateUserAccess(UserEntity user) {
return new UserEntity(user);
}
}
board-style.css
.justify-content-center ul{
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 0;
list-style: none;
}
boardWrite
document.addEventListener('DOMContentLoaded', function () {
const jwtToken = getItemWithExpiry("jwtToken");
fetch('/users/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `${jwtToken}`
}
})
.then(response => response.json())
.then(userData => {
document.getElementById('board-form').addEventListener('submit', function (event) {
event.preventDefault();
const title = document.getElementById('card-title').value;
const content = document.getElementById('card-text').value;
const requestBody = {
title: title,
content: content,
user: {
user_id: userData.userId,
}
};
fetch(`/api/boards`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `${jwtToken}`
},
body: JSON.stringify(requestBody)
})
.then(response => response.json())
.then(data => {
if (data) {
console.log(data)
alert("게시글이 성공적으로 작성되었습니다.");
window.location.href = "/boards";
} else {
alert("게시글 작성에 실패했습니다.");
}
})
.catch(error => console.error('Error:', error));
});
})
.catch(error => {
console.error('사용자 정보를 가져오는 데 실패했습니다.', error);
});
});
board-write
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Board Edit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/css/header-style.css">
<link rel="stylesheet" type="text/css" href="/css/board-write.css">
</head>
<body>
<header>
<div id="nav-buttons" class="text-right p-3">
<!-- 네비게이션 버튼이 필요하다면 여기서 동적으로 추가 -->
</div>
</header>
<div class="container">
<h1 class="my-4 text-center">Edit Board Post</h1>
<!-- 게시글 수정 폼 -->
<div class="card">
<h3 class="mb-4">게시물 작성</h3>
<form id="board-form" method="post">
<div class="form-group">
<label for="card-title">제목:</label>
<input type="text" class="form-control" id="card-title" name="title" required/>
</div>
<div class="form-group">
<label for="card-text">내용:</label>
<textarea class="form-control" id="card-text" name="content" rows="10" required></textarea>
</div>
<div class="text-right">
<button type="submit" class="btn btn-custom">저장</button>
</div>
</form>
</div>
<!-- 링크를 통해 목록으로 돌아가기 -->
<div class="text-center mt-4">
<a class="btn btn-outline-secondary" href="/boards">Back to List</a>
</div>
</div>
<script src="/scriptPage/js/userMe.js"></script>
<script src="/scriptPage/js/boardWrite.js"></script>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Board List</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/css/header-style.css">
<link rel="stylesheet" type="text/css" href="/css/board-style.css">
<script src="/scriptPage/js/userMe.js"></script>
</head>
<body>
<header>
<div id="nav-buttons">
<!-- 이 부분은 자바스크립트로 동적으로 업데이트됩니다. -->
</div>
</header>
<div class="container">
<h1 class="my-4">Board List</h1>
<!-- 게시판 테이블 -->
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Content</th>
<th>Created At</th>
<th>Modified At</th>
</tr>
</thead>
<tbody> <!-- 게시글 목록을 반복하여 표시 -->
<tr th:each="post : ${boardList}">
<td><a th:href="@{/boards/{id}(id=${post.boardId})}" th:text="${post.boardId}">1</a></td>
<td><a th:href="@{/boards/{id}(id=${post.boardId})}" th:text="${post.title}">Sample Title</a></td>
<td><a th:href="@{/boards/{id}(id=${post.boardId})}" th:text="${post.content}">Sample Content</a></td>
<td><a th:href="@{/boards/{id}(id=${post.boardId})}" th:text="${post.createdAt}">2024-08-14</a></td>
<td><a th:href="@{/boards/{id}(id=${post.boardId})}" th:text="${post.modifiedAt}">2024-08-14</a></td>
</tr>
</tbody>
</table>
<!-- 페이지네이션 -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${pageNumber == 1} ? 'disabled'">
<a class="page-link" th:href="@{/boards(page=${pageNumber - 10 < 1 ? 1 : pageNumber - 10}, size=${pageSize})}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li th:with="startPage=${((pageNumber - 1) / 10) * 10 + 1}, endPage=${startPage + 9}, totalPages=${totalPages}">
<ul>
<li class="page-item" th:each="i : ${#numbers.sequence(startPage, endPage)}"
th:if="${i <= totalPages}"
th:classappend="${i == pageNumber} ? 'active'">
<a class="page-link" th:href="@{/boards(page=${i}, size=${pageSize})}" th:text="${i}">1</a>
</li>
</ul>
</li>
<li class="page-item" th:classappend="${pageNumber == totalPages} ? 'disabled'">
<a class="page-link" th:href="@{/boards(page=${pageNumber + 10 > totalPages ? totalPages : pageNumber + 10}, size=${pageSize})}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
<!-- 작성하기 -->
<div id="board-buttons">
</div></div>
<script src="/scriptPage/js/userMe.js"></script>
<script src="/scriptPage/js/checkLogined.js"></script>
</body>
</html>