728x90
로그인
출저 : https://shinsunyoung.tistory.com/78 이분 정말 감사드립니다.
이분의 블로그를 많이 참조하였습니다.
1. Spring Security
보안 솔루션을 제공해주는 Spring 기반의 하위 프레임워크입니다.
따른 설정 없이 간단하게 유효성 검사를 대신해주는 친구라고 생각해도 될꺼같아요!
틀렸다면 반드시 댓글에 남겨주세요..
이 친구를 이해하기전에 인증과 권한도 알아봅시다.
2. 인증 & 권한
인증 - 이 친구가 정말 이친구인가?
권한 - 접근이 가능하도록 해준다.
위 Security 를 사용하면 인증과 권한을 쉽게 사용할수있다는 장점이있습니다.
3. Spring Security를 사용하는 이유
- 모든 URL에 대한 인증을 요구
- 로그인 폼을 생성, 로그아웃 처리
- CSRF 공격을 방어
- Session Fixation 공격 방어
- 요청 헤더 보안
- HTTP Strict Transport Security (HSTS)
- X-Content-Type-Options
- 캐시 컨트롤 (정적 리소스는 캐싱)
- X-XSS-Protection
- 클릭 재킹 방지를위한 X-Frame-Options
- 서블릿 API 메소드와 통합
- HttpServletRequest#getRemoteUser()
- HttpServletRequest.html#getUserPrincipal()
- HttpServletRequest.html#isUserInRole(java.lang.String)
- [HttpServletRequest.html#login(java.lang.String, java.lang.String)](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String, java.lang.String))
- HttpServletRequest.html#logout()
소스코드 리뷰
1. Security
SecurityConfig.java는 자신의 폴더
( com.example.sever.config.Security.Config.java)
package talk.server.config;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;
import talk.server.jwt.CustomAuthenticationEntryPoint;
import talk.server.jwt.JwtAuthenticationFilter;
import talk.server.jwt.JwtTokenProvider;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
// .cors().configurationSource(corsConfigurationSource())
// .and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.antMatchers("/board/user", "/webSocket/**", "/**").permitAll()
.anyRequest().hasRole("USER") // 그외 나머지 요청은 모두 인증된 회원만 접근 가능
.and()
.cors()
.and()
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.addFilterBefore(new JwtAuthenticationFilter(authenticationManager(), jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
}
// @Bean
// public CorsConfigurationSource corsConfigurationSource() {
// CorsConfiguration configuration = new CorsConfiguration();
// // - (3)
// configuration.addAllowedOriginPattern("*");
// configuration.addAllowedMethod("*");
// configuration.addAllowedHeader("*");
// configuration.setAllowCredentials(true);
// configuration.setMaxAge(3600L);
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// source.registerCorsConfiguration("/**", configuration);
// return source;
//
// }
}
2. Controller
com.example.sever.Controller.Apicontroller
package talk.server.controller;
import talk.server.jwt.JwtTokenProvider;
import talk.server.service.TbService;
import talk.server.service.UserService;
import talk.server.vo.Tb;
import talk.server.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
@RequestMapping("/board")
@RestController
public class ApiController {
@Autowired
private TbService service;
@Autowired
private UserService userService;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@GetMapping(value = "/boardlist")
public ResponseEntity<ArrayList<Tb>> getAllTb() {
ArrayList<Tb> list = service.getAllTb();
if (list != null) return new ResponseEntity<>(list, HttpStatus.OK);
else return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@GetMapping("/user")
// @PreAuthorize("hasAnyRole('USER','ADMIN')")
public ResponseEntity<User> getUser(@RequestParam String id) {
// User user = userService.getUser(id);
// jwtTokenProvider.createToken(user.getUsername(), user.getAuthorities());
return ResponseEntity.ok(userService.getUser(id));
}
}
3. dao
com.example.sever.UserDao
package talk.server.dao;
import talk.server.vo.User;
public interface UserDao {
public User getUser(String id);
}
4. jwt
com.example.sever.jwt.JwtAuthenticationFilter
package talk.server.jwt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String BEARER_PREFIX = "Bearer ";
@Autowired
private JwtTokenProvider jwtTokenProvider;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider) {
super(authenticationManager);
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Enumeration<String> headerNames = httpRequest.getHeaderNames();
System.out.println("------------------------------------");
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
System.out.println("Header: " + httpRequest.getHeader(headerNames.nextElement()));
}
}
System.out.println("------------------------------------");
String token = resolveToken((HttpServletRequest) request);
System.out.println("doFilter -> " + token);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
// @Override
// public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
// throws IOException, ServletException {
//
// HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
// Enumeration<String> headerNames = httpRequest.getHeaderNames();
//
// System.out.println("------------------------------------");
// if (headerNames != null) {
// while (headerNames.hasMoreElements()) {
// System.out.println("Header: " + httpRequest.getHeader(headerNames.nextElement()));
// }
// }
// System.out.println("------------------------------------");
// String token = resolveToken((HttpServletRequest) servletRequest);
// System.out.println("doFilter -> " + token);
//
// if (token != null && jwtTokenProvider.validateToken(token)) {
// Authentication auth = jwtTokenProvider.getAuthentication(token);
// SecurityContextHolder.getContext().setAuthentication(auth);
// }
//
// filterChain.doFilter(servletRequest, servletResponse);
// }
// Request Header 에서 토큰 정보를 꺼내오기
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7);
}
return null;
}
}
com.example.sever.jwt.JwtTokenProvider
package talk.server.jwt;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import talk.server.service.CustomUserDetailService;
import javax.annotation.PostConstruct;
import java.util.Base64;
import java.util.Collection;
import java.util.Date;
@Component
public class JwtTokenProvider {
private final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
private String secretKey = "auth";
private long tokenValidMilisecond = 1000L * 60 * 60; // 1시간만 토큰 유효
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
@Autowired
private CustomUserDetailService customUserDetailService;
// Jwt 토큰 생성
public String createToken(String userPk, Collection<? extends GrantedAuthority> roles) {
Claims claims = Jwts.claims().setSubject(userPk);
claims.put("roles", roles);
Date now = new Date();
return Jwts.builder()
.setClaims(claims) // 데이터
.setIssuedAt(now) // 토큰 발행일자
.setExpiration(new Date(now.getTime() + tokenValidMilisecond)) // set Expire Time
.signWith(SignatureAlgorithm.HS256, secretKey) // 암호화 알고리즘, secret값 세팅
.compact();
}
public Authentication getAuthentication(String token) {
System.out.println(token);
UserDetails userDetails = customUserDetailService.loadUserByUsername(this.getUserPk(token));
System.out.println(userDetails);
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getUserPk(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
// public String resolveToken(HttpServletRequest req) {
// return req.getHeader("X-AUTH-TOKEN");
// }
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return !claims.getBody().getExpiration().before(new Date());
} catch (SignatureException e) {
logger.info("유효하지 않은 JWT signature 입니다.");
} catch (ExpiredJwtException e) {
logger.info("만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
logger.info("지원되지 않는 JWT 토큰입니다.");
} catch (IllegalArgumentException e) {
logger.info("JWT 토큰이 잘못되었습니다.");
}
return false;
}
}
com.example.sever.jwt.StompHandler
package talk.server.jwt;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.stereotype.Component;
import java.util.Objects;
@Component
@RequiredArgsConstructor
public class StompHandler implements ChannelInterceptor {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
System.out.println("message:" + message);
System.out.println("헤더 : " + message.getHeaders());
System.out.println("토큰" + accessor.getNativeHeader("Authorization"));
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
jwtTokenProvider.validateToken(Objects.requireNonNull(accessor.getFirstNativeHeader("Authorization")).substring(7));
}
return message;
}
}
5.service
com.example.sever.service.UserService
package talk.server.service;
import talk.server.vo.User;
public interface UserService {
public User getUser(String id);
}
com.example.sever.service.UserServiceImpl
package talk.server.service;
import talk.server.dao.UserDao;
import talk.server.jwt.JwtTokenProvider;
import talk.server.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao dao;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Override
public User getUser(String id) {
User user = dao.getUser(id);
String token = jwtTokenProvider.createToken(user.getUsername(), user.getAuthorities());
List<String> list = new ArrayList<>();
list.add("ROLE_USER");
list.add("ADMIN");
user.setRoles(list);
user.setAuth(token);
return user;
}
}
6. vo
com.example.sever.vo.User.java
package talk.server.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {
private String id;
private String password;
private String name;
private String auth;
private int enabled;
private List<String> roles = new ArrayList<>();
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
authList.add(new SimpleGrantedAuthority("ROLE_USER"));
return authList;
}
@Override
public String getUsername() {
return this.id;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
728x90
'개발 > 술Talk' 카테고리의 다른 글
[Spring & Maria DB] 회원 정보 수정, 회원 정보 리스트 받아오기 (0) | 2021.08.18 |
---|---|
[Spring & Maria DB] Restful API 게시판 CRUD (0) | 2021.08.12 |
window 환경 Docker - Kurento ( 도커 - 쿠렌토 ) 사용하기 (11) | 2021.08.03 |
[ BE - JS ] 로그인, 회원가입, 로그아웃 구현 (0) | 2021.08.02 |
[ 술 Talk ] - 프로젝트 1주차 진행 내용 (0) | 2021.07.16 |