拆分think模块包
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package com.gxwebsoft.common.core.security;
|
||||
|
||||
import com.gxwebsoft.common.core.Constants;
|
||||
import com.gxwebsoft.common.core.utils.CommonUtil;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 没有访问权限异常处理
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2020-03-25 00:35:03
|
||||
*/
|
||||
@Component
|
||||
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
|
||||
throws IOException, ServletException {
|
||||
CommonUtil.responseError(response, Constants.UNAUTHORIZED_CODE, Constants.UNAUTHORIZED_MSG, e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gxwebsoft.common.core.security;
|
||||
|
||||
import com.gxwebsoft.common.core.Constants;
|
||||
import com.gxwebsoft.common.core.utils.CommonUtil;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 没有登录异常处理
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2020-03-25 00:35:03
|
||||
*/
|
||||
@Component
|
||||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
|
||||
throws IOException, ServletException {
|
||||
CommonUtil.responseError(response, Constants.UNAUTHENTICATED_CODE, Constants.UNAUTHENTICATED_MSG,
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.gxwebsoft.common.core.security;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.gxwebsoft.common.core.Constants;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
import com.gxwebsoft.common.core.utils.CommonUtil;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.core.utils.SignCheckUtil;
|
||||
import com.gxwebsoft.common.system.entity.LoginRecord;
|
||||
import com.gxwebsoft.common.system.entity.Menu;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.common.system.service.LoginRecordService;
|
||||
import com.gxwebsoft.common.system.service.UserService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
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.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 处理携带token的请求过滤器
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2020-03-30 20:48:05
|
||||
*/
|
||||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
@Resource
|
||||
private ConfigProperties configProperties;
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
@Resource
|
||||
private LoginRecordService loginRecordService;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
String access_token = JwtUtil.getAccessToken(request);
|
||||
if (StrUtil.isNotBlank(access_token)) {
|
||||
try {
|
||||
// 解析token
|
||||
Claims claims = JwtUtil.parseToken(access_token, configProperties.getTokenKey());
|
||||
JwtSubject jwtSubject = JwtUtil.getJwtSubject(claims);
|
||||
|
||||
// 校验服务器域名白名单
|
||||
final SignCheckUtil checkUtil = new SignCheckUtil();
|
||||
String key = "WhiteDomain:" + jwtSubject.getTenantId();
|
||||
List<String> whiteDomains = redisUtil.get(key, List.class);
|
||||
if (!checkUtil.checkWhiteDomains(whiteDomains, request.getServerName())) {
|
||||
throw new UsernameNotFoundException("The requested domain name is not on the whitelist");
|
||||
}
|
||||
|
||||
User user = userService.getByUsername(jwtSubject.getUsername(), jwtSubject.getTenantId());
|
||||
if (user == null) {
|
||||
throw new UsernameNotFoundException("Username not found");
|
||||
}
|
||||
List<Menu> authorities = user.getAuthorities().stream()
|
||||
.filter(m -> StrUtil.isNotBlank(m.getAuthority())).collect(Collectors.toList());
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||
user, null, authorities);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
// token将要过期签发新token, 防止突然退出登录
|
||||
long expiration = (claims.getExpiration().getTime() - new Date().getTime()) / 1000 / 60;
|
||||
if (expiration < configProperties.getTokenRefreshTime()) {
|
||||
String token = JwtUtil.buildToken(jwtSubject, configProperties.getTokenExpireTime(),
|
||||
configProperties.getTokenKey());
|
||||
response.addHeader(Constants.TOKEN_HEADER_NAME, token);
|
||||
loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_REFRESH, null,
|
||||
user.getTenantId(), request);
|
||||
}
|
||||
} catch (ExpiredJwtException e) {
|
||||
CommonUtil.responseError(response, Constants.TOKEN_EXPIRED_CODE, Constants.TOKEN_EXPIRED_MSG,
|
||||
e.getMessage());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
CommonUtil.responseError(response, Constants.BAD_CREDENTIALS_CODE, Constants.BAD_CREDENTIALS_MSG,
|
||||
e.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.gxwebsoft.common.core.security;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Jwt载体
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2021-09-03 00:11:12
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class JwtSubject implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 租户id
|
||||
*/
|
||||
private Integer tenantId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.gxwebsoft.common.core.security;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import com.gxwebsoft.common.core.Constants;
|
||||
import com.gxwebsoft.common.core.utils.JSONUtil;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* JWT工具类
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2018-01-21 16:30:59
|
||||
*/
|
||||
public class JwtUtil {
|
||||
|
||||
/**
|
||||
* 获取请求中的access_token
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @return String
|
||||
*/
|
||||
public static String getAccessToken(HttpServletRequest request) {
|
||||
String access_token = ServletUtil.getHeaderIgnoreCase(request, Constants.TOKEN_HEADER_NAME);
|
||||
if (StrUtil.isNotBlank(access_token)) {
|
||||
if (access_token.startsWith(Constants.TOKEN_TYPE)) {
|
||||
access_token = StrUtil.removePrefix(access_token, Constants.TOKEN_TYPE).trim();
|
||||
}
|
||||
} else {
|
||||
access_token = request.getParameter(Constants.TOKEN_PARAM_NAME);
|
||||
}
|
||||
return access_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
*
|
||||
* @param subject 载体
|
||||
* @param expire 过期时间
|
||||
* @param base64EncodedKey base64编码的Key
|
||||
* @return token
|
||||
*/
|
||||
public static String buildToken(JwtSubject subject, Long expire, String base64EncodedKey) {
|
||||
return buildToken(JSONUtil.toJSONString(subject), expire, decodeKey(base64EncodedKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
*
|
||||
* @param subject 载体
|
||||
* @param expire 过期时间
|
||||
* @param key 密钥
|
||||
* @return token
|
||||
*/
|
||||
public static String buildToken(String subject, Long expire, Key key) {
|
||||
Date expireDate = new Date(new Date().getTime() + 1000 * expire);
|
||||
return Jwts.builder()
|
||||
.setSubject(subject)
|
||||
.setExpiration(expireDate)
|
||||
.setIssuedAt(new Date())
|
||||
.signWith(key)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析token
|
||||
*
|
||||
* @param token token
|
||||
* @param base64EncodedKey base64编码的Key
|
||||
* @return Claims
|
||||
*/
|
||||
public static Claims parseToken(String token, String base64EncodedKey) {
|
||||
return parseToken(token, decodeKey(base64EncodedKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析token
|
||||
*
|
||||
* @param token token
|
||||
* @param key 密钥
|
||||
* @return Claims
|
||||
*/
|
||||
public static Claims parseToken(String token, Key key) {
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(key)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JwtSubject
|
||||
*
|
||||
* @param claims Claims
|
||||
* @return JwtSubject
|
||||
*/
|
||||
public static JwtSubject getJwtSubject(Claims claims) {
|
||||
return JSONUtil.parseObject(claims.getSubject(), JwtSubject.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Key
|
||||
*
|
||||
* @return Key
|
||||
*/
|
||||
public static Key randomKey() {
|
||||
return Keys.secretKeyFor(SignatureAlgorithm.HS256);
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码key
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String encodeKey(Key key) {
|
||||
return Encoders.BASE64.encode(key.getEncoded());
|
||||
}
|
||||
|
||||
/**
|
||||
* base64编码Key
|
||||
*
|
||||
* @param base64EncodedKey base64编码的key
|
||||
* @return Key
|
||||
*/
|
||||
public static Key decodeKey(String base64EncodedKey) {
|
||||
if (StrUtil.isBlank(base64EncodedKey)) {
|
||||
return null;
|
||||
}
|
||||
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64EncodedKey));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.gxwebsoft.common.core.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
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.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Spring Security配置
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2020-03-23 18:04:52
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Resource
|
||||
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
|
||||
@Resource
|
||||
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
|
||||
@Resource
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
.antMatchers(HttpMethod.OPTIONS, "/**")
|
||||
.permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/api/file/**","/**", "/api/captcha", "/")
|
||||
.permitAll()
|
||||
.antMatchers(
|
||||
"/api/login",
|
||||
"/api/register",
|
||||
"/api/existence",
|
||||
"/api/oss/upload",
|
||||
"/druid/**",
|
||||
"/swagger-resources/**",
|
||||
"/webjars/**",
|
||||
"/hxz/v1/**",
|
||||
"/api/sendSmsCaptcha",
|
||||
"/api/loginBySms",
|
||||
"/api/parseToken/*",
|
||||
"/api/login-alipay/*",
|
||||
"/api/wx-login/loginByMpWxPhone",
|
||||
"/api/wx-login/loginByOpenId",
|
||||
"/api/system/wx-native-pay/**",
|
||||
"/api/system/wx-pay/**",
|
||||
"/api/wxWorkQrConnect",
|
||||
"/api/sys/user-plan-log/wx-pay/**",
|
||||
"/api/wx-official/**",
|
||||
"/lvQ4EoivKJ.txt"
|
||||
)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.csrf()
|
||||
.disable()
|
||||
.cors()
|
||||
.and()
|
||||
.logout()
|
||||
.disable()
|
||||
.headers()
|
||||
.frameOptions()
|
||||
.disable()
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.accessDeniedHandler(jwtAccessDeniedHandler)
|
||||
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
|
||||
.and()
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user