Skip to content

Commit

Permalink
整合JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronlinv committed Aug 22, 2021
1 parent 97f9d10 commit a6c52f0
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 8 deletions.
11 changes: 11 additions & 0 deletions Dockerfile
@@ -0,0 +1,11 @@
FROM openjdk:8u201-jdk-alpine3.9

ENV LANG=C.UTF-8 LC_ALL=C.UTF-8

VOLUME /tmp

ADD target/_01SpringSecurityDemo01-0.0.1-SNAPSHOT.jar app.jar

ENTRYPOINT ["java","-jar","app.jar"]

EXPOSE 8081
6 changes: 6 additions & 0 deletions pom.xml
Expand Up @@ -59,6 +59,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>

<build>
Expand Down
@@ -0,0 +1,37 @@
package com.al._01springsecuritydemo01.common;

import java.util.HashMap;

/**
* API 接口返回结果封装
*/
public class RestResult extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
// 状态码
public static final String CODE_TAG = "code";
// 返回内容
public static final String MSG_TAG = "msg";
// 数据对象
public static final String DATA_TAG = "data";

public RestResult() {

}

public RestResult(int code, String msg) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}

public RestResult(int code, String msg, Object data) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (data != null) {
super.put(DATA_TAG, data);
}
}

public static RestResult success() {
return new RestResult(200, "成功");
}
}
Expand Up @@ -2,15 +2,20 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.al._01springsecuritydemo01.config.jwt.JwtAuthTokenFilter;
import com.al._01springsecuritydemo01.service.UserDetailService;

@EnableWebSecurity
Expand All @@ -24,21 +29,40 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailService userDetailService;

@Autowired
private JwtAuthTokenFilter jwtAuthTokenFilter;

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
// .defaultSuccessUrl("/index")
// .defaultSuccessUrl("/index")
.successHandler(successHandler)
.failureHandler(failureHandler)
// http.formLogin()
// .loginPage("/login.html")
// .loginProcessingUrl("/login")
// // .defaultSuccessUrl("/index")
// // .defaultSuccessUrl("/index")
// .successHandler(successHandler)
// .failureHandler(failureHandler)
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()

.authorizeRequests()
.antMatchers("/login")
.anonymous()

.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js")
.permitAll()

.and()
.authorizeRequests()
Expand All @@ -58,6 +82,7 @@ protected void configure(HttpSecurity http) throws Exception {
.and()
.csrf().disable();
http.logout().logoutUrl("/logout");
http.addFilterBefore(jwtAuthTokenFilter, UsernamePasswordAuthenticationFilter.class);
}

@Override
Expand Down
@@ -0,0 +1,29 @@
package com.al._01springsecuritydemo01.config.jwt;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import com.al._01springsecuritydemo01.entity.User;

@Service
public class JwtAuthService {
@Autowired
private JwtTokenUtils jwtTokenUtils;

@Autowired
private AuthenticationManager authenticationManager;

public String login(String username, String password) {
Authentication authentication = null;
try {
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (Exception e) {
throw new RuntimeException("用户名或密码有误");
}
User loginUser = (User) authentication.getPrincipal();
return jwtTokenUtils.generateToken(loginUser);
}
}
@@ -0,0 +1,45 @@
package com.al._01springsecuritydemo01.config.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;

@Component
public class JwtAuthTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;

@Autowired
private JwtTokenUtils jwtTokenUtils;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String jwtToken = request.getHeader(jwtTokenUtils.getHeader());

if (!ObjectUtils.isEmpty(jwtToken)) {
String username = jwtTokenUtils.getUsernameFromToken(jwtToken);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtils.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
// 交给SpringSecurity管理,在之后的过滤器不会被拦截进行二次授权了
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
chain.doFilter(request, response);
}
}
@@ -0,0 +1,168 @@
package com.al._01springsecuritydemo01.config.jwt;


import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Component
@Slf4j
public class JwtTokenUtils {

@Value("${token.secret}")
private String secret;

@Value("${token.expireTime}")
private Long expiration;

@Value("${token.header}")
private String header;


private static Key KEY = null;

/**
* 生成token令牌
*
* @param userDetails 用户
* @return 令token牌
*/
public String generateToken(UserDetails userDetails) {
log.info("[JwtTokenUtils] generateToken " + userDetails.toString());
Map<String, Object> claims = new HashMap<>(2);
claims.put("sub", userDetails.getUsername());
claims.put("created", new Date());

return generateToken(claims);
}


/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
String username = null;
try {
Claims claims = getClaimsFromToken(token);
username = claims.get("sub", String.class);
log.info("从令牌中获取用户名:" + username);
} catch (Exception e) {
username = null;
}
return username;
}

/**
* 判断令牌是否过期
*
* @param token 令牌
* @return 是否过期
*/
public Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
}

/**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put("created", new Date());


refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}

/**
* 验证令牌
*
* @param token 令牌
* @param userDetails 用户
* @return 是否有效
*/
public Boolean validateToken(String token, UserDetails userDetails) {

String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) &&
!isTokenExpired(token));
}


/**
* 从claims生成令牌,如果看不懂就看谁调用它
*
* @param claims 数据声明
* @return 令牌
*/
private String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration);
return Jwts.builder().setClaims(claims)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS256, getKeyInstance())
.compact();
}

/**
* 从令牌中获取数据声明,如果看不懂就看谁调用它
*
* @param token 令牌
* @return 数据声明
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;

try {
claims = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token).getBody();
// claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}


private Key getKeyInstance() {
if (KEY == null) {
synchronized (JwtTokenUtils.class) {
if (KEY == null) {// 双重锁
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(secret);
KEY = new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
}
}
}
return KEY;
}
}

0 comments on commit a6c52f0

Please sign in to comment.