jwt
- 토큰을 발행, 정보추출용으로 사용한다
- 헤더, 페이로드, 시그니쳐로 이루어져 있다.
package com.example.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.example.jwt.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
@WebFilter(urlPatterns = { "/api/customer/mypage" })
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
JwtUtil jwtUtil;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
try {
// 토큰 가져오기
String token = request.getHeader("TOKEN");
if (token != null) {
if (token.length() > 0) {
// 토큰을 이용해서 아이디 추출하기
String username = jwtUtil.extractUsername(token);
// 토큰 검증
System.out.println("jwtRequestFilter : " + token);
System.out.println("username : " + username);
// 컨트롤러로 이동
filterChain.doFilter(request, response);
}
}
// 토큰 없으면 오류발생
else {
throw new Exception("토큰없음");
}
} catch (Exception e) {
e.printStackTrace();
response.sendError(-1, "토큰오류");
}
}
}
package com.example.jwt;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.stereotype.Service;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
// 토큰을 발행 및 정보추출용
@Service
public class JwtUtil {
private final String SECURITY_KEY = "fjehje#$4343";
// 1000 => 1초
private final long VALIDATE_TIME = 1000 * 60 * 60 * 9; // 9H
// 토큰 생성(아이디 정보)
public String generatorToken(String username) {
Map<String, Object> map = new HashMap<>();
String token = Jwts.builder()
.setClaims(map)
.setSubject(username) // 토큰제목
.setIssuedAt(new Date(System.currentTimeMillis())) // 토큰생성일
.setExpiration(new Date(System.currentTimeMillis() + VALIDATE_TIME)) // 토큰유효시간
.signWith(SignatureAlgorithm.HS256, SECURITY_KEY)
.compact();
return token;
}
// 정보 추출용 메소드
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = Jwts.parser().setSigningKey(SECURITY_KEY).parseClaimsJws(token).getBody();
return claimsResolver.apply(claims);
}
// 토큰에서 아이디 추출
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
// 토큰에서 만료시간 추출
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
// 유효시간 체크
public boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
// 토큰이 유효한지 체크
public boolean isTokenValidation(String token, String uid) {
String username = extractUsername(token);
if (username.equals(uid) && isTokenExpired(token)) {
return true;
}
return false;
}
}
security
- configuration을 통해서 접근권한을 설정한다.
- UserDetailServiceImpl를 통해서 권한을 확인할 수 있도록 한다.
package com.example.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import com.example.service.UserDetailsServiceImpl;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 1. 직접 만든 detailService 객체 가져오기
@Autowired
UserDetailsServiceImpl detailsService;
// 회원가입시 암호화 했던 방법의 객체생성
// 2. 암호화 방법 객체 생성, @Bean은 서버 구동시 자동으로 객체 생성됨
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
// 3. 직접만든 detailsService에 암호화 방법 적용
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
// 페이지별 접근 권한 설정
http.authorizeRequests()
.antMatchers("/admin", "/admin/**")
.hasAuthority("ADMIN")
.antMatchers("/seller", "/seller/**")
.hasAnyAuthority("ADMIN", "SELLER")
.antMatchers("/customer", "/customer/**")
.hasAuthority("CUSTOMER")
.anyRequest().permitAll();
// 로그인 페이지 설정, 단 POST는 직접 만들지 않음
http.formLogin()
.loginPage("/member/login")
.loginProcessingUrl("/member/loginaction")
.usernameParameter("uemail")
.passwordParameter("upw")
.defaultSuccessUrl("/home")
.permitAll();
// 로그아웃 페이지 설정, url에 맞게 POST로 호출하면 됨.
http.logout()
.logoutUrl("/member/logout")
.logoutSuccessUrl("/home")
// .logoutSuccessHandler(new MyLogoutSuccessHandler())
.invalidateHttpSession(true)
.clearAuthentication(true)
.permitAll();
// 접근권한불가 403
http.exceptionHandling().accessDeniedPage("/page403");
// h2-console을 사용하기 위해서
http.csrf().ignoringAntMatchers("/h2-console/**");
http.headers().frameOptions().sameOrigin();
// rest controller 사용
http.csrf().ignoringAntMatchers("/api/**");
}
}
user
- security의 filter와 같은 느낌이다.
- user의 아이디를 통해서 데이터를 가져온다.
package com.example.service;
import java.util.Collection;
import com.example.dto.MemberDTO;
import com.example.mapper.MemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
// 로그인에서 버튼을 누르면 컨트롤을 통과해서 서비스로 이메일이 전달됨
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
MemberMapper mMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("UserDetails" + username);
MemberDTO member = mMapper.memberEmail(username);
String[] strRole = { member.getUrole() };
Collection<GrantedAuthority> roles = AuthorityUtils.createAuthorityList(strRole);
User user = new User(username, member.getUpw(), roles);
return user;
}
}