- Spring Web
- Lombok
- Spring Dev Tool
- OAuth
- JPA
- Security
- MySQL Driver
<!-- 추가 라이브러리 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<!-- JSP 템플릿 엔진 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>9.0.43</version>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
create user 'pos'@'%' identified by 'pos1234';
GRANT ALL PRIVILEGES ON *.* TO 'pos'@'%';
create database pos;
@RequiredArgsConstructor
@Configuration // IoC등록
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
private final OAuth2DetailsService oAuth2DetailsService;
// IoC등록만 하면 AuthenticationManager가 Bcrypt로 자동 검증해줌.
@Bean
public BCryptPasswordEncoder encode() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**","/post/**","/reply/**").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')") // ROLE_는 강제성이 있음.
.antMatchers("/admin").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin() // x-www-form-urlencoded 로 전송 (Json으로 던지면 못 받음)
.loginPage("/loginForm")
.loginProcessingUrl("/login")// 스프링 시큐리티가 /login 주소가 들어오면 낚아챔
.defaultSuccessUrl("/") // x-www-urlencoded (어느 경로로 갈려고하는데 인증에 막히면 인증 후 그 경로로 감)
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(oAuth2DetailsService);
}
}
OAuth 로그인(구글, 페이스북, 네이버, 카카오)
private OAuth2User processOAuth2User(OAuth2UserRequest userRequest, OAuth2User oauth2User) {
//1번 통합 클래스를 생성
OAuth2UserInfo oAuth2UserInfo = null;
System.out.println("뭐로 로그인?" + userRequest.getClientRegistration().getClientName());
if(userRequest.getClientRegistration().getClientName().equals("Google")) {
oAuth2UserInfo=new GoogleInfo(oauth2User.getAttributes());
} else if(userRequest.getClientRegistration().getClientName().equals("Facebook")) {
oAuth2UserInfo=new FacebookInfo(oauth2User.getAttributes());
} else if(userRequest.getClientRegistration().getClientName().equals("Naver")) {
oAuth2UserInfo = new NaverInfo((Map)(oauth2User.getAttributes().get("response")));
} else if(userRequest.getClientRegistration().getClientName().equals("Kakao")) {
oAuth2UserInfo = new KakaoInfo(oauth2User.getAttributes());
}
// 2번 최초 : 회원가입 + 로그인, 최초x : 로그인
User userEntity = userRepository.findByUsername(oAuth2UserInfo.getUsername());
UUID uuid = UUID.randomUUID();
String encPassword = new BCryptPasswordEncoder().encode(uuid.toString());
if(userEntity == null) { // DB에 없으면 최초 접속(회원가입)
User user = User.builder()
.username(oAuth2UserInfo.getUsername())
.password(encPassword)
.email(oAuth2UserInfo.getEmail())
.role(RoleType.USER)
.build();
userEntity = userRepository.save(user);
return new PrincipalDetails(userEntity, oauth2User.getAttributes());
}else { // 이미 회원가입이 완료됐다는 뜻(원래는 구글 정보가 변경될 수 있기 때문에 update 해야되는데 지금은 안하겠음)
return new PrincipalDetails(userEntity, oauth2User.getAttributes());
}
}
@RequiredArgsConstructor
@Service
public class AuthService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Transactional
public void 회원가입(User user) {
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
user.setRole(RoleType.USER);
userRepository.save(user);
}
}
@GetMapping("/")
public String findAll(Model model,
@PageableDefault(sort = "id",direction = Sort.Direction.DESC, size = 10) Pageable pageable,
@AuthenticationPrincipal PrincipalDetails principalDetails, @RequestParam(required = false, defaultValue = "") String keyword) {
//Page<Post> posts = postService.전체찾기(pageable);
Page<Post> posts = postService.검색하기(pageable,keyword);
model.addAttribute("posts",posts);
return "post/list"; // 기본적으로 return은 forwarding
}
public interface PostRepository extends JpaRepository<Post, Integer>{
Page<Post> findByTitleContainingOrContentContaining(Pageable pageable, String title, String content);
}
썸머노트 에디터 이용
- 댓글작성, 삭제
- 자기가 쓴 글일 경우 수정, 삭제 가능
$("#btn-reply-save").on("click",(e)=>{
let data = {
content:$("#reply-content").val(),
postId:e.currentTarget.value
};
$.ajax({
type:"POST",
url:"/reply",
data:JSON.stringify(data),
contentType:"application/json; charset=utf-8",
dataType:"json"
}).done((res)=>{
console.log(res);
if(res.statusCode===1){
history.go(0);
} else {
alert("댓글 등록에 실패하셨습니다.");
}
});
});
function deleteReply(id){
$.ajax({
type:"DELETE",
url:"/reply/"+id,
dataType:"json"
}).done((res)=>{
console.log(res);
if(res.statusCode===1){
$("#reply-"+id).remove();
} else {
alert("수정에 실패하였습니다.");
}
});
}
게시글 수정, 삭제의 경우 FORM태그에서 PUT, DELETE를 지원하지 않기 때문에 Ajax통신 사용
$("#btn-delete").on("click",(e)=>{
let id = e.currentTarget.value;
$.ajax({
type:"DELETE",
url:"/post/"+id,
dataType:"json"
}).done(res=>{
if(res.statusCode === 1){
alert("삭제에 성공하였습니다.");
history.back();
} else {
alert("삭제에 실패했습니다.");
}
});
});
$("#btn-update").on("click",(e)=>{
let id = $("#id").val();
let data = {
title:$("#title").val(),
content:$("#content").val()
};
$.ajax({
type:"PUT",
url:"/post/"+id,
data:JSON.stringify(data),
contentType:"application/json; charset=utf-8",
dataType:"json"
}).done((res)=>{
console.log(res);
if(res.statusCode===1){
alert("수정에 성공하였습니다.");
history.go(-1);
} else {
alert("수정에 실패하였습니다.");
}
});
});
패스워드와 email 수정가능