Index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import Root from './Root';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Root/>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Page.js
import { ThemeContext } from '@emotion/react';
import React, { useContext } from 'react';
import Content from './Content';
import Footer from './Footer';
import Header from './Header';
// function Page({isDark,setIsDark}) {
// console.log(isDark);
// return (
// <div className='page'>
// <Header isDark={isDark}/>
// <Content isDark={isDark}/>
// <Footer isDark={isDark} setIsDark={setIsDark}/>
// </div>
// );
// }
function Page() {
// const data=useContext(ThemeContext);
// console.log("data"+data); //값 확인
return (
<div className='page'>
<Header/>
<Content/>
<Footer/>
</div>
);
}
export default Page;
Root.js
import React, { useState } from 'react';
import './App.css';
import Page from './Page';
import { ThemeContext } from './context/ThemeContext';
import { UserContext } from './context/UserContext';
function Root(props) {
const [isDark,setIsDark]=useState(false);
return (
// <div>
// {/* Page는 실제 isDark변수를 사용안함. 단지 서브컴포넌트로 전달하기 위해서 props로 보냄 */}
// <Page isDark={isDark} setIsDark={setIsDark}/>
// </div>
<UserContext.Provider value={'네이바클라우드'}>
<ThemeContext.Provider value={{isDark,setIsDark}}>
<Page/>
</ThemeContext.Provider>
</UserContext.Provider>
);
}
export default Root;
src>context>ThemeContext.js
import { createContext } from "react";
export const ThemeContext=createContext(null);
src>context>UserContext.js
import {createContext} from "react";
export const UserContext=createContext(null);
Header.js
import { ThemeContext } from '@emotion/react';
import React, { useContext } from 'react';
import './App.css';
import { UserContext } from './context/UserContext';
// function Header({isDark}) {
// return (
// <header className='header' style={{backgroundColor:isDark?'black':'lightgray'}}>
// <h1>Welcome 비트캠프!</h1>
// </header>
// );
// }
function Header() {
const {isDark}=useContext(ThemeContext);
const user=useContext(UserContext);
return (
<header className='header' style={{backgroundColor:isDark?'black':'lightgray'}}>
<h1>Welcome {user}!</h1>
</header>
);
}
export default Header;
Content.js
import React, { useContext } from 'react';
import './App.css';
import { ThemeContext } from './context/ThemeContext';
import { UserContext } from './context/UserContext';
// function Content({isDark}) {
// return (
// <div className='content' style={{backgroundColor:isDark?'gray':'white'}}>
// <p>비트캠프님! 즐거운 하루되세요!!!</p>
// </div>
// );
// }
function Content() {
const {isDark}=useContext(ThemeContext);
const user=useContext(UserContext);
return (
<div className='content' style={{backgroundColor:isDark?'gray':'white'}}>
<p>{user}님! 즐거운 하루되세요!!!</p>
</div>
);
}
export default Content;
Footer.js
import React, { useContext } from 'react';
import './App.css';
import { ThemeContext } from './context/ThemeContext';
// function Footer({isDark,setIsDark}) {
// const toggleButton=()=>{
// console.log("click:"+isDark);
// setIsDark(!isDark);
// }
// return (
// <footer className='footer' style={{backgroundColor:isDark?'black':'lightgray'}}>
// <button type='button' className='button'
// onClick={toggleButton}>Dark Mode</button>
// </footer>
// );
// }
function Footer() {
const {isDark,setIsDark}=useContext(ThemeContext);
const toggleButton=()=>{
console.log("click:"+isDark);
setIsDark(!isDark);
}
return (
<footer className='footer' style={{backgroundColor:isDark?'black':'lightgray'}}>
<button type='button' className='button'
onClick={toggleButton}>Dark Mode</button>
</footer>
);
}
export default Footer;
-----------------------------------------------------------------
JWT Token
JwtAuthenticationEntryPoint.java
package jwt.setting;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* 유효한 자격증명을 하지 않고 접근하려 할때 401.
*
* @param request
* @param response
* @param e
* @throws IOException
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* 유효한 자격증명을 하지 않고 접근하려 할때 401.
*
* @param request
* @param response
* @param e
* @throws IOException
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
JwtAuthentificationFilter.java
package jwt.setting;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.info(request.getRequestURI());
if(!request.getRequestURI().contains("login") && !request.getRequestURI().contains("favicon")) {
log.info("토큰 체크");
try {
String jwt = getJwtFromRequest(request); //request에서 jwt 토큰을 꺼낸다.
if (StringUtils.isNotEmpty(jwt) && JwtTokenProvider.validateToken(jwt)) {
String userId = JwtTokenProvider.getUserIdFromJWT(jwt); //jwt에서 사용자 id를 꺼낸다.
log.info("userId : " + userId);
UserAuthentication authentication = new UserAuthentication(userId, null, null); //id를 인증한다.
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //기본적으로 제공한 details 세팅
SecurityContextHolder.getContext()
.setAuthentication(authentication); //세션에서 계속 사용하기 위해 securityContext에 Authentication 등록
} else {
if (StringUtils.isEmpty(jwt)) {
//request.setAttribute("unauthorization", "401 인증키 없음.");
System.out.println("401 인증키 없음.");
}
if (JwtTokenProvider.validateToken(jwt)) {
//request.setAttribute("unauthorization", "401-001 인증키 만료.");
System.out.println("401-001 인증키 만료.");
}
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
log.info("bearerToken : " + bearerToken);
if (StringUtils.isNotEmpty(bearerToken) && bearerToken.startsWith("Bearer ")) {
log.info("Bearer exist");
return bearerToken.substring("Bearer ".length());
}
return null;
}
}
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.info(request.getRequestURI());
if(!request.getRequestURI().contains("login") && !request.getRequestURI().contains("favicon")) {
log.info("토큰 체크");
try {
String jwt = getJwtFromRequest(request); //request에서 jwt 토큰을 꺼낸다.
if (StringUtils.isNotEmpty(jwt) && JwtTokenProvider.validateToken(jwt)) {
String userId = JwtTokenProvider.getUserIdFromJWT(jwt); //jwt에서 사용자 id를 꺼낸다.
log.info("userId : " + userId);
UserAuthentication authentication = new UserAuthentication(userId, null, null); //id를 인증한다.
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //기본적으로 제공한 details 세팅
SecurityContextHolder.getContext()
.setAuthentication(authentication); //세션에서 계속 사용하기 위해 securityContext에 Authentication 등록
} else {
if (StringUtils.isEmpty(jwt)) {
//request.setAttribute("unauthorization", "401 인증키 없음.");
System.out.println("401 인증키 없음.");
}
if (JwtTokenProvider.validateToken(jwt)) {
//request.setAttribute("unauthorization", "401-001 인증키 만료.");
System.out.println("401-001 인증키 만료.");
}
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
log.info("bearerToken : " + bearerToken);
if (StringUtils.isNotEmpty(bearerToken) && bearerToken.startsWith("Bearer ")) {
log.info("Bearer exist");
return bearerToken.substring("Bearer ".length());
}
return null;
}
}
JwtTokenProvider.java
package jwt.setting;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class JwtTokenProvider {
//https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx
private static final String JWT_SECRET = "KbPeShVmYq3t6w9z$C&F)H@McQfTjWnZ";
// 토큰 유효시간
private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; // 30분
private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7; // 7일
private static Key secretKey;
@PostConstruct
protected void init() {
secretKey = Keys.hmacShaKeyFor(JWT_SECRET.getBytes(StandardCharsets.UTF_8));
}
// jwt 토큰 생성
public static String generateToken(Authentication authentication,String userId) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + ACCESS_TOKEN_EXPIRE_TIME);
return Jwts.builder()
.setSubject((String) authentication.getPrincipal()) // 사용자
.setIssuedAt(new Date()) // 현재 시간 기반으로 생성
.setExpiration(expiryDate) // 만료 시간 세팅
.claim("userId", userId)
.claim("userName", "비트캠프")
// 사용할 암호화 알고리즘, signature에 들어갈 secret 값 세팅
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}
// Jwt 토큰에서 아이디 추출
public static String getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
log.info("id:"+claims.getId());
log.info("issuer:"+claims.getIssuer());
log.info("issue:"+claims.getIssuedAt().toString());
log.info("subject:"+claims.getSubject());
log.info("Audience:"+claims.getAudience());
log.info("expire:"+claims.getExpiration().toString());
log.info("userName:"+claims.get("userName"));
return claims.getSubject();
}
// Jwt 토큰 유효성 검사
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return true;
}catch (io.jsonwebtoken.security.SecurityException|MalformedJwtException e) {
log.error("잘못된 JWT 서명입니다.", e.getMessage());
} catch (ExpiredJwtException e) {
log.error("만료된 JWT 토큰입니다.", e.getMessage());
} catch (UnsupportedJwtException e) {
log.error("지원되지 않는 JWT 토큰입니다.", e.getMessage());
} catch (IllegalArgumentException e) {
log.error("JWT 토큰이 잘못되었습니다.", e.getMessage());
}catch(Exception e) {
log.error("에러 메세지", e.getMessage());
}
return false;
}
}
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class JwtTokenProvider {
//https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx
private static final String JWT_SECRET = "KbPeShVmYq3t6w9z$C&F)H@McQfTjWnZ";
// 토큰 유효시간
private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30; // 30분
private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7; // 7일
private static Key secretKey;
@PostConstruct
protected void init() {
secretKey = Keys.hmacShaKeyFor(JWT_SECRET.getBytes(StandardCharsets.UTF_8));
}
// jwt 토큰 생성
public static String generateToken(Authentication authentication,String userId) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + ACCESS_TOKEN_EXPIRE_TIME);
return Jwts.builder()
.setSubject((String) authentication.getPrincipal()) // 사용자
.setIssuedAt(new Date()) // 현재 시간 기반으로 생성
.setExpiration(expiryDate) // 만료 시간 세팅
.claim("userId", userId)
.claim("userName", "비트캠프")
// 사용할 암호화 알고리즘, signature에 들어갈 secret 값 세팅
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}
// Jwt 토큰에서 아이디 추출
public static String getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
log.info("id:"+claims.getId());
log.info("issuer:"+claims.getIssuer());
log.info("issue:"+claims.getIssuedAt().toString());
log.info("subject:"+claims.getSubject());
log.info("Audience:"+claims.getAudience());
log.info("expire:"+claims.getExpiration().toString());
log.info("userName:"+claims.get("userName"));
return claims.getSubject();
}
// Jwt 토큰 유효성 검사
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return true;
}catch (io.jsonwebtoken.security.SecurityException|MalformedJwtException e) {
log.error("잘못된 JWT 서명입니다.", e.getMessage());
} catch (ExpiredJwtException e) {
log.error("만료된 JWT 토큰입니다.", e.getMessage());
} catch (UnsupportedJwtException e) {
log.error("지원되지 않는 JWT 토큰입니다.", e.getMessage());
} catch (IllegalArgumentException e) {
log.error("JWT 토큰이 잘못되었습니다.", e.getMessage());
}catch(Exception e) {
log.error("에러 메세지", e.getMessage());
}
return false;
}
}
SecurityConfiguration.java
package jwt.setting;
import java.util.Arrays;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Slf4j
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtAuthenticationEntryPoint unauthorizedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// (1) 교차출처 리소스 공유(CORS) 설정
.cors() //(1)
.and()
// (2) CSRF(Cross Site Request Forgery) 사이트 간 요청 위조 설정
.csrf() //(2)
.disable()
// 인증, 허가 에러 시 공통적으로 처리해주는 부분
.exceptionHandling() //(3)
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement() //(4)
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// UsernamePasswordAuthenticationFilter보다 JwtAuthenticationFilter를 먼저 수행
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests() // (5)
// login, 회원가입 API는 토큰이 없는 상태에서 요청이 들어오기 때문에 permitAll
// .antMatchers("/member/**","/board/list/**","/board/detail/**")
.antMatchers("/member/**","/board/list/**")
.permitAll()
.antMatchers("/").permitAll()
// 나머지는 전부 인증 필요
.antMatchers("/**")
.authenticated()
// 시큐리티는 기본적으로 세션을 사용
// 여기서는 세션을 사용하지 않기 때문에 세션 설정을 Stateless 로 설정
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE"));
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
// Spring security룰을 무시하게 하는 url규칙
// @Override
// public void configure(WebSecurity web) {
// System.out.println("web security");
// web.ignoring()
// .antMatchers("/h2-console/**", "/favicon.ico")
// .antMatchers("/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html", "/webjars/**", "/swagger/**");
// // .antMatchers("/resources/**")
// // .antMatchers("/css/**")
// // .antMatchers("/vendor/**")
// // .antMatchers("/js/**")
// // .antMatchers("/favicon*/**")
// // .antMatchers("/img/**")
// }
//비밀번호 암호화를 위한 Encoder 설정
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
import java.util.Arrays;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Slf4j
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtAuthenticationEntryPoint unauthorizedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// (1) 교차출처 리소스 공유(CORS) 설정
.cors() //(1)
.and()
// (2) CSRF(Cross Site Request Forgery) 사이트 간 요청 위조 설정
.csrf() //(2)
.disable()
// 인증, 허가 에러 시 공통적으로 처리해주는 부분
.exceptionHandling() //(3)
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement() //(4)
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// UsernamePasswordAuthenticationFilter보다 JwtAuthenticationFilter를 먼저 수행
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests() // (5)
// login, 회원가입 API는 토큰이 없는 상태에서 요청이 들어오기 때문에 permitAll
// .antMatchers("/member/**","/board/list/**","/board/detail/**")
.antMatchers("/member/**","/board/list/**")
.permitAll()
.antMatchers("/").permitAll()
// 나머지는 전부 인증 필요
.antMatchers("/**")
.authenticated()
// 시큐리티는 기본적으로 세션을 사용
// 여기서는 세션을 사용하지 않기 때문에 세션 설정을 Stateless 로 설정
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE"));
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
// Spring security룰을 무시하게 하는 url규칙
// @Override
// public void configure(WebSecurity web) {
// System.out.println("web security");
// web.ignoring()
// .antMatchers("/h2-console/**", "/favicon.ico")
// .antMatchers("/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html", "/webjars/**", "/swagger/**");
// // .antMatchers("/resources/**")
// // .antMatchers("/css/**")
// // .antMatchers("/vendor/**")
// // .antMatchers("/js/**")
// // .antMatchers("/favicon*/**")
// // .antMatchers("/img/**")
// }
//비밀번호 암호화를 위한 Encoder 설정
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserAuthentication.java
package jwt.setting;
import java.util.List;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
public class UserAuthentication extends UsernamePasswordAuthenticationToken {
public UserAuthentication(String principal, String credentials) {
super(principal, credentials);
}
public UserAuthentication(String principal, String credentials,
List<GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
import java.util.List;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
public class UserAuthentication extends UsernamePasswordAuthenticationToken {
public UserAuthentication(String principal, String credentials) {
super(principal, credentials);
}
public UserAuthentication(String principal, String credentials,
List<GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
BoardDetailPage.js
import Axios from 'axios';
import React, {useCallback, useEffect, useState} from 'react';
import { useNavigate, useParams } from 'react-router-dom';
function BoardDetailPage(props) {
const [dto,setDto]=useState({});
const {num,currentPage}=useParams();
const navi=useNavigate();
const photourl=process.env.REACT_APP_BOARDURL;
const myid=sessionStorage.myid;
const loginok=sessionStorage.loginok;
// const selectData=()=>{
// const url=`/board/detail?num=${num}`;
// // Axios.get(url)
// // .then(res=>{
// // setDto(res.data);
// // })
// Axios({
// type:'get',
// url,
// headers: {Authorization:`Bearer ${sessionStorage.token}`},
// }).then(res=>{
// setDto(res.data);
// })
// },[num])
const selectData=useCallback(()=>{
const url=`/board/detail?num=${num}`;
// Axios.get(url)
// .then(res=>{
// setDto(res.data);
// })
Axios({
type:'get',
url,
headers: {Authorization:`Bearer ${sessionStorage.token}`},
}).then(res=>{
setDto(res.data);
})
},[num])
useEffect(()=>{
if(sessionStorage.token==null){
alert("게시판 내용은 회원만 확인 가능합니다\n먼저 로그인을 해주세요");
navi("/login");
return;
}
selectData();
},[selectData]);
return (
<div style={{marginLeft:'30px',width:'600px'}}>
<h5><b>{dto.subject}</b></h5>
<h6>
<span>작성자:{dto.myname}({dto.myid})</span>
<span style={{float:'right',color:'gray'}}>
조회 {dto.readcount}
{dto.writeday}
</span>
</h6>
{
dto.photo==null?'':
<img alt='' src={`${photourl}${dto.photo}`}
style={{border:'1px solid gray',maxWidth:'500px'}}/>
}
<br/><br/>
<pre>{dto.content}</pre>
<br/>
<div>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/form")}>글쓰기</button>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/list/1")}>목록</button>
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
const url=`/board/delete?num=${dto.num}`;
Axios.delete(url)
.then(res=>{
//목록으로 이동
navi(`/board/list/${currentPage}`)
})
}}>삭제</button>:''
}
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
}}>수정</button>:''
}
</div>
</div>
);
}
export default BoardDetailPage;
import React, {useCallback, useEffect, useState} from 'react';
import { useNavigate, useParams } from 'react-router-dom';
function BoardDetailPage(props) {
const [dto,setDto]=useState({});
const {num,currentPage}=useParams();
const navi=useNavigate();
const photourl=process.env.REACT_APP_BOARDURL;
const myid=sessionStorage.myid;
const loginok=sessionStorage.loginok;
// const selectData=()=>{
// const url=`/board/detail?num=${num}`;
// // Axios.get(url)
// // .then(res=>{
// // setDto(res.data);
// // })
// Axios({
// type:'get',
// url,
// headers: {Authorization:`Bearer ${sessionStorage.token}`},
// }).then(res=>{
// setDto(res.data);
// })
// },[num])
const selectData=useCallback(()=>{
const url=`/board/detail?num=${num}`;
// Axios.get(url)
// .then(res=>{
// setDto(res.data);
// })
Axios({
type:'get',
url,
headers: {Authorization:`Bearer ${sessionStorage.token}`},
}).then(res=>{
setDto(res.data);
})
},[num])
useEffect(()=>{
if(sessionStorage.token==null){
alert("게시판 내용은 회원만 확인 가능합니다\n먼저 로그인을 해주세요");
navi("/login");
return;
}
selectData();
},[selectData]);
return (
<div style={{marginLeft:'30px',width:'600px'}}>
<h5><b>{dto.subject}</b></h5>
<h6>
<span>작성자:{dto.myname}({dto.myid})</span>
<span style={{float:'right',color:'gray'}}>
조회 {dto.readcount}
{dto.writeday}
</span>
</h6>
{
dto.photo==null?'':
<img alt='' src={`${photourl}${dto.photo}`}
style={{border:'1px solid gray',maxWidth:'500px'}}/>
}
<br/><br/>
<pre>{dto.content}</pre>
<br/>
<div>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/form")}>글쓰기</button>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/list/1")}>목록</button>
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
const url=`/board/delete?num=${dto.num}`;
Axios.delete(url)
.then(res=>{
//목록으로 이동
navi(`/board/list/${currentPage}`)
})
}}>삭제</button>:''
}
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
}}>수정</button>:''
}
</div>
</div>
);
}
export default BoardDetailPage;
'학교 & 학원 이론 수업 > 네이버 클라우드 AIaaS 개발자 양성 과정' 카테고리의 다른 글
final project (0) | 2023.07.25 |
---|---|
react (10) useReducer , useCallback (0) | 2023.06.28 |
react (9) - 페이징 처리 (0) | 2023.06.26 |
react(8) - 로그인 구현 (0) | 2023.06.23 |
react(6),(7) - docker, Jenkins, docker hub (0) | 2023.06.22 |