백은 스프링부트에 시큐리티랑 jpa를 붙이긴할거다.
일단 테스트만 할거니
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
;
return http.build();
}
}
비활성만 하고
server:
port: 8081
servlet:
encoding:
charset: UTF-8
enabled: true
force: true
spring:
application:
name: board
datasource:
url: jdbc:log4jdbc:mysql://${DBHOST}:${DBPORT}/${DB}
username: ${USERNAME}
password: ${PASSWORD}
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
hikari:
maximum-pool-size: 3
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show-sql: false
format_sql: false
logging:
level:
com:
zaxxer:
hikari: ERROR
javax:
sql:
DataSource: OFF
jdbc:
audit: OFF
resultset: OFF
result settable: info
sql only: false
sliding: OFF
connection: OFF
org:
hibernate:
SQL: ERROR
type:
descriptor:
sql:
BasicBinder: OFF
common:
jwt:
secret: ${SECRET}
issuer: ${ISSUER}
이거도 자동배포 시킬거니 환경변수에 다 입력하고
환경변수는 인텔리제이 기준

옵션수정 누르면 나오더라
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx")
private int idx;
@Column(name = "user_id", unique = true)
private String userId;
@Column(name = "password")
private String password;
}
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
연결만 확인하고
@Auth어노테이션을 만들어
controller에 @Auth가 붙으면 필수 @("isOptional=true") 이면 인증이 필수는아닌거로 구분
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auth {
public boolean isOptional() default false;
}
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod handlerMethod)) {
return true;
}
Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
if(auth != null) {
String authToken = request.getHeader("Authorization");
if (StringUtils.hasText(authToken) && authToken.startsWith("Bearer ")) {
authToken = authToken.substring(7);
}
if(!StringUtils.hasText(authToken)){
if(!auth.isOptional()){
sendErrorResponse(response,new ResponseDTO(HttpStatus.FORBIDDEN, ErrorConst.REQUIRED_AUTH.getCode(), ErrorConst.REQUIRED_AUTH.getMessage()));
return false;
}
}
else{
ResponseDTO dto = jwtUtil.validateToken(authToken, request);
if(!dto.isResultCode()){
sendErrorResponse(response,dto);
return false;
}
}
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
private void sendErrorResponse(HttpServletResponse response, ResponseDTO dto) throws IOException {
response.setStatus(dto.getErrorCode());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(new ObjectMapper().writeValueAsString(dto));
}
}
public class ResponseDTO<T> {
boolean resultCode = true;
String message = "성공";
T object;
int errorCode = 0;
public ResponseDTO(T object) {
this.object = object;
}
public ResponseDTO(HttpStatus status, T object) {
this.resultCode = false;
this.message = "실패";
this.object = object;
this.errorCode = status.value();
}
public ResponseDTO(HttpStatus status, T object, String message) {
this.resultCode = false;
this.message = message;
this.object = object;
this.errorCode = status.value();
}
}
@Component
public class JwtUtil {
@Value("${common.jwt.secret}")
String SECRET;
public ResponseDTO validateToken(String token, HttpServletRequest request) {
SecretKey key = Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
try {
// Bearer 접두사 제거
if (token.startsWith("Bearer ")) {
token = token.substring(7);
}
// JWT 파싱 및 검증
Claims claims = Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(token)
.getPayload();
// 토큰 유효성 검사
Date expiration = claims.getExpiration();
if (expiration.before(new Date())) {
return new ResponseDTO(HttpStatus.UNAUTHORIZED,ErrorConst.EXPRIED_TOKEN.getCode(),ErrorConst.EXPRIED_TOKEN.getMessage());
}
// 발행자(issuer) 검증
String issuer = claims.getIssuer();
if (!"board-auth".equals(issuer)) {
return new ResponseDTO(HttpStatus.UNAUTHORIZED,ErrorConst.INVALID_TOKEN.getCode(),ErrorConst.INVALID_TOKEN.getMessage());
}
LoginDTO dto = new LoginDTO();
dto.setIdx((Integer) claims.get("idx"));
dto.setUserId((String) claims.get("user_id"));
ArrayList<SimpleGrantedAuthority> authList = new ArrayList<>();
authList.add(new SimpleGrantedAuthority("ROLE_" + "USER"));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(dto, dto.getPassword(), authList);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
return new ResponseDTO();
} catch (ExpiredJwtException e) {
return new ResponseDTO(HttpStatus.UNAUTHORIZED,ErrorConst.EXPRIED_TOKEN.getCode(),ErrorConst.EXPRIED_TOKEN.getMessage());
} catch (UnsupportedJwtException | MalformedJwtException | SecurityException | IllegalArgumentException e) {
return new ResponseDTO(HttpStatus.UNAUTHORIZED,ErrorConst.INVALID_TOKEN.getCode(),ErrorConst.INVALID_TOKEN.getMessage());
} catch (Exception e) {
return new ResponseDTO(HttpStatus.INTERNAL_SERVER_ERROR,ErrorConst.UNKNOWN_ERROR.getCode(),ErrorConst.UNKNOWN_ERROR.getMessage());
}
}
}
일단 필요한거 만들었고
누가바로 테스트인 컨트롤러
@RestController
@RequiredArgsConstructor
@Slf4j
public class AuthController {
private final UserService userService;
@Auth
@GetMapping("/test")
public String loginCheck() {
return "test";
}
@Auth(isOptional = true)
@GetMapping("/test2")
public String loginCheck2() {
return "test2";
}
@GetMapping("/test3")
public String loginCheck3() {
return "test3";
}
}

토큰없이

토큰포함

옵셔널에도 토큰없이
근데 람다에 갱신을 안만들었구나
RefreshFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: auth/
Handler: app.refresh
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DYNAMODB
Events:
Login:
Type: Api
Properties:
Path: /refresh
Method: post
LogoutFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: auth/
Handler: app.logout
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DYNAMODB
Events:
Login:
Type: Api
Properties:
Path: /logout
Method: post
dev.yaml
export const refresh = async (event) => {
const body = JSON.parse(event.body)
const refreshToken = body.refreshToken;
let connection = await createConnection();
try {
const decoded = jwt.verify(refreshToken, process.env.SECRET, {issuer: process.env.ISSUER});
console.log(decoded)
if (decoded.idx) {
try {
const dynamoDbSelectParams = {
TableName: dynamoDbTable,
Key: {
"user_idx": decoded.idx,
"refresh_token": refreshToken
}
};
const dynamoDbData = await docClient.get(dynamoDbSelectParams).promise();
if (!dynamoDbData.Item) {
connection.destroy();
return createResponse(404, {message: 'NOT_FOUND'});
}
console.log("refresh userSelect idx : "+decoded.idx)
const [user] = await connection.query(
'select * from user where idx = ?;',
[decoded.idx]
)
console.log("refresh user : "+user)
if (user) {
delete user.password;
const token = jwt.sign(user, process.env.SECRET, tokenOptions);
const newRefreshToken = jwt.sign(user, process.env.SECRET, refreshTokenOptions);
const date = new Date();
date.setDate(date.getDate() + 30);
const dynamoDbParams = {
TableName: dynamoDbTable,
Item: {
"user_idx": user.idx,
"refresh_token": newRefreshToken,
"expireTimestamp": Math.floor(date.getTime() / 1000)
}
};
await docClient.put(dynamoDbParams).promise();
const dynamoDbDeleteParams = {
TableName: dynamoDbTable,
Key: {
"user_idx": decoded.idx,
"refresh_token": refreshToken
}
};
await docClient.delete(dynamoDbDeleteParams).promise();
connection.destroy();
return createResponse(200, {token, refreshToken: newRefreshToken, decoded});
} else {
connection.destroy();
return createResponse(404, {message: 'NOT_FOUND'});
}
} catch (err) {
connection.destroy();
return createResponse(500, {message: err.message});
}
} else {
connection.destroy();
return createResponse(500, {message: 'PARAMETER_ERROR'});
}
} catch (err) {
connection.destroy();
if (err.name === 'TokenExpiredError') {
return createResponse(403);
}
return createResponse(500);
}
}
export const logout = async (event) => {
const body = JSON.parse(event.body)
const refreshToken = body.refreshToken;
let connection = await createConnection();
try {
const decoded = jwt.verify(refreshToken, process.env.SECRET, {issuer: process.env.ISSUER});
if (decoded.idx) {
try {
const dynamoDbDeleteParams = {
TableName: dynamoDbTable,
Key: {
"user_idx": decoded.idx,
"refresh_token": refreshToken
}
};
await docClient.delete(dynamoDbDeleteParams).promise();
connection.destroy();
return createResponse(200);
} catch (err) {
connection.destroy();
return createResponse(500, err);
}
} else {
connection.destroy();
return createResponse(500, {message: 'PARAMETER_ERROR'});
}
} catch (err) {
connection.destroy();
return createResponse(500, err);
}
}
app.js에 추가하고
성공응답 확인 리액트에 붙이는건 내일하자...