SpringBoot + SpringSecurity 와 JWT 인증 및 권한 부여
14461 단어 springsecurityjwtspringboot
블 로그: jwt 와 session 의 차이 점 과 장점 을 바탕 으로
jwt 의 역할 을 알 고 jwt 가 어떻게 생 성 되 었 는 지 살 펴 보 자.
1: 우선 Maven 에 관련 의존 도입
org.springframework.boot
spring-boot-starter-security
io.jsonwebtoken
jjwt
0.9.0
2: Jwt 생 성 도구 클래스 (참고)
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author yangfan
* @description
*
* Jwt
* JWT :header.payload.signature
* header ( 、token ):
* {"alg": "HS512","typ": "JWT"}
* payload ( 、 、 ):
* {"sub":"wang","created":1489079981393,"exp":1489684781}
* signature :
* HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
*
* @modified By
*/
@Component
@Slf4j
public class JwtTokenUtil {
//
private static final String CLAIM_KEY_USERNAME = "sub";
//
private static final String CLAIM_KEY_CREATED = "created";
//jwt
@Value("${jwt.secret}")
private String secret;
//jwt
@Value("${jwt.expiration}")
private Long expiration;
/**
* JWT token
* subject , JSON
* issuer
* claims
*/
private String generateToken(Map claims) {
return Jwts.builder()
//
.setClaims(claims)
//
.setExpiration(generateExpirationDate())
//
.signWith(SignatureAlgorithm.HS512, secret)
// : JWT , , json , userid,roldid , 。
//.setSubject(subject)
//
//.setIssuer(Optional.ofNullable(issuer).orElse(ISS))
.compact();
}
/**
* token JWT
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
log.info("JWT :{}",token);
}
return claims;
}
/**
* token
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* token
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* token
*
* @param token token
* @param userDetails
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* token
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/**
* token
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/**
* token
* @param userDetails
*
*/
public String generateToken(UserDetails userDetails) {
Map claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/**
* token
*/
public boolean canRefresh(String token) {
return !isTokenExpired(token);
}
/**
* token
*/
public String refreshToken(String token) {
Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
}
3: 지금 우 리 는 전단 계 정 비밀 번 호 를 통 해 로그 인 합 니 다. 이 로그 인 은 인터페이스 가 위의 jwtTokenUtil 을 통 해 token 을 생 성 합 니 다.
/**
* , token
* @param username
* @param password
* @return String jwt
*/
@Override
public String login(String username, String password) {
String token = null;
try {
//
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// , BCryptPasswordEncoder PasswordEncoder
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException(" ");
}
/**
* UsernamePasswordAuthenticationToken AbstractAuthenticationToken Authentication
* UsernamePasswordAuthenticationToken (Authentication),
* Authentication AuthenticationManager
* AuthenticationManager AuthenticationProvider,
* Provider UserDetailsService UserDetail
* UsernamePasswordAuthenticationToken Authentication
*/
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
token = jwtTokenUtil.generateToken(userDetails);
} catch (AuthenticationException e) {
log.warn(" :{}", e.getMessage());
}
return token;
}
4: 위 login 인터페이스 에서 생 성 된 token 을 전단 으로 보 내 고 전단 에서 백 엔 드 인터페이스 에 접근 할 때마다 이 token 을 가지 고 있 습 니 다. 보통 header 머리 에 놓 습 니 다.
이 필 터 는 필터 요청 입 니 다. token 이 유효 하고 합 법 적 인지 검증 한 후에 해당 하 는 사용자 권한 을 가 져 옵 니 다.
SecurityContextHolder
에 현재 방문 자의 정 보 를 저장 합 니 다.import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author yangfan
* @description JWT
* @modified By
*/
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
String username = jwtTokenUtil.getUserNameFromToken(authToken);
log.info("checking username:{}", username);
//SecurityContextHolder ,
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// SpringSecurityConfig , UserDetailsService loadUserByUsername ,
// , AdminUserDetails
// AdminUserDetails UserDetails , UserDetails
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
// token 、
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
/**
* springsecurity username password ,
* , WebAuthenticationDetails ,
* HttpServletRequest
*
*/
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
log.info("authenticated user:{}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
}
5: 중점 이 왔 습 니 다. 지금부터 spring security 설정 류 를 작성 하려 면 위의 jwt 필 터 를 spring security 에 통합 해 야 합 니 다.
/**
* @author yangfan
* @description SpringSecurity , csrf ,druid
* @modified By
*
* |@EnableGlobalMethodSecurity(prePostEnabled=true) , controller
* |Controller @PreAuthorize("hasAuthority('pms:brand:read')") ,
* | , Authentication
* |SecurityExpressionRoot getAuthoritySet()
*
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
// ,
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
// token ,
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
/**
* url 、jwt .
* @param httpSecurity
* @throws Exception
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable() // CSRF
.sessionManagement()// token, session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, //
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/swagger-resources/**",
"/v2/api-docs/**"
)
.permitAll()
.antMatchers("/admin/login", "/admin/register")//
.permitAll()
.antMatchers(HttpMethod.OPTIONS)// options
.permitAll()
.antMatchers("/druid/*")// druid , CSRF (Cross—Site Request Forgery) , druid
.permitAll()
.anyRequest()//
.authenticated();
//
httpSecurity.headers().cacheControl();
// JWT filter
httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//
httpSecurity.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint);
}
/**
* UserDetailsService PasswordEncoder
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
/**
* SpringSecurity , BCryptPasswordEncoder
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* , ,
* @return
*/
@Bean
public UserDetailsService userDetailsService() {
//
return username -> {
UmsAdmin admin = adminService.getAdminByUsername(username);
if (admin != null) {
List permissionList = adminService.getPermissionList(admin.getId());
return new AdminUserDetails(admin,permissionList);
}
throw new UsernameNotFoundException(" ");
};
}
/**
* , jwt token, token .
* @return
*/
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
return new JwtAuthenticationTokenFilter();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
자, 생각 은 바로 이런 생각 입 니 다. 완전한 코드 논 리 를 보고 싶 습 니 다.
전체 코드: 키 즈 입 니 다. 같이 푸 른 달 놀 러 오 세 요.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.