修复语音对讲功能推流鉴权key不存在问题,修复了JWT判断token过期问题
parent
f56cf67f52
commit
46a6d7bc38
|
@ -1,13 +1,19 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.VManageBootstrap;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.Role;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.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.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
|
@ -18,6 +24,8 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.genersoft.iot.vmp.conf.security.dto.JwtUser.TokenStatus.NORMAL;
|
||||
|
||||
/**
|
||||
* jwt token 过滤器
|
||||
*/
|
||||
|
@ -25,75 +33,47 @@ import java.util.ArrayList;
|
|||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
// 忽略登录请求的token验证
|
||||
String requestURI = request.getRequestURI();
|
||||
if ((requestURI.startsWith("/doc.html") || requestURI.startsWith("/swagger-ui") ) && !userSetting.getDocEnable()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
if (requestURI.equalsIgnoreCase("/api/user/login")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userSetting.isInterfaceAuthentication()) {
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
// String requestURI = request.getRequestURI();
|
||||
// if ((requestURI.startsWith("/doc.html") || requestURI.startsWith("/swagger-ui") ) && !userSetting.getDocEnable()) {
|
||||
// response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
// return;
|
||||
// }
|
||||
// if (requestURI.equalsIgnoreCase("/api/user/login")) {
|
||||
// chain.doFilter(request, response);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (!userSetting.isInterfaceAuthentication()) {
|
||||
// UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
|
||||
// SecurityContextHolder.getContext().setAuthentication(token);
|
||||
// chain.doFilter(request, response);
|
||||
// return;
|
||||
// }
|
||||
String jwt = request.getHeader(JwtUtils.getHeader());
|
||||
// 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
|
||||
// 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
jwt = request.getParameter(JwtUtils.getHeader());
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
jwt = request.getHeader(JwtUtils.getApiKeyHeader());
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
if(jwt != null) {
|
||||
JwtUser jwtUser = JwtUtils.verifyToken(jwt);
|
||||
String username = jwtUser.getUserName();
|
||||
|
||||
if(jwtUser.getStatus() == NORMAL) {
|
||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
}
|
||||
}
|
||||
|
||||
JwtUser jwtUser = JwtUtils.verifyToken(jwt);
|
||||
String username = jwtUser.getUserName();
|
||||
// TODO 处理各个状态
|
||||
switch (jwtUser.getStatus()){
|
||||
case EXPIRED:
|
||||
response.setStatus(400);
|
||||
chain.doFilter(request, response);
|
||||
// 异常
|
||||
return;
|
||||
case EXCEPTION:
|
||||
// 过期
|
||||
response.setStatus(400);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
case EXPIRING_SOON:
|
||||
// 即将过期
|
||||
// return;
|
||||
default:
|
||||
}
|
||||
|
||||
// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
|
||||
User user = new User();
|
||||
user.setId(jwtUser.getUserId());
|
||||
user.setUsername(jwtUser.getUserName());
|
||||
user.setPassword(jwtUser.getPassword());
|
||||
Role role = new Role();
|
||||
role.setId(jwtUser.getRoleId());
|
||||
user.setRole(role);
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, jwtUser.getPassword(), new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.io.InputStreamReader;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -108,6 +109,69 @@ public class JwtUtils implements InitializingBean {
|
|||
return rsaJsonWebKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从token中获取用户名
|
||||
*
|
||||
*/
|
||||
public static String getUserNameFromToken(String token){
|
||||
String username = null;
|
||||
|
||||
JwtUser jwtUser = new JwtUser();
|
||||
|
||||
try {
|
||||
JwtConsumer consumer = new JwtConsumerBuilder()
|
||||
.setAllowedClockSkewInSeconds(30)
|
||||
.setRequireSubject()
|
||||
.setExpectedAudience(AUDIENCE)
|
||||
.setVerificationKey(rsaJsonWebKey.getPublicKey())
|
||||
.build();
|
||||
|
||||
JwtClaims claims = consumer.processToClaims(token);
|
||||
NumericDate expirationTime = claims.getExpirationTime();
|
||||
if (expirationTime != null) {
|
||||
// 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
|
||||
// 剩余时间 (秒)
|
||||
long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
|
||||
if (timeRemaining < 5 * 60) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
|
||||
} else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
|
||||
}
|
||||
} else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
|
||||
}
|
||||
|
||||
Long apiKeyId = claims.getClaimValue("apiKeyId", Long.class);
|
||||
if (apiKeyId != null) {
|
||||
UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(apiKeyId.intValue());
|
||||
if (userApiKey == null || !userApiKey.isEnable()) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
}
|
||||
}
|
||||
|
||||
username = (String) claims.getClaimValue("userName");
|
||||
User user = userService.getUserByUsername(username);
|
||||
|
||||
jwtUser.setUserName(username);
|
||||
jwtUser.setPassword(user.getPassword());
|
||||
jwtUser.setRoleId(user.getRole().getId());
|
||||
jwtUser.setUserId(user.getId());
|
||||
|
||||
return username;
|
||||
} catch (InvalidJwtException e) {
|
||||
if (e.hasErrorCode(ErrorCodes.EXPIRED)) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
} else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION);
|
||||
}
|
||||
return username;
|
||||
} catch (Exception e) {
|
||||
logger.error("[Token解析失败]: {}", e.getMessage());
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
return username;
|
||||
}
|
||||
}
|
||||
|
||||
public static String createToken(String username, Long expirationTime, Map<String, Object> extra) {
|
||||
try {
|
||||
/*
|
||||
|
@ -176,13 +240,12 @@ public class JwtUtils implements InitializingBean {
|
|||
.setExpectedAudience(AUDIENCE)
|
||||
.setVerificationKey(rsaJsonWebKey.getPublicKey())
|
||||
.build();
|
||||
|
||||
JwtClaims claims = consumer.processToClaims(token);
|
||||
NumericDate expirationTime = claims.getExpirationTime();
|
||||
if (expirationTime != null) {
|
||||
// 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
|
||||
// 剩余时间 (秒)
|
||||
long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
|
||||
long timeRemaining = expirationTime.getValue() - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
|
||||
if (timeRemaining < 5 * 60) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
|
||||
} else {
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
|
@ -32,7 +34,6 @@ public class SecurityUtils {
|
|||
LoginUser user = (LoginUser) authenticate.getPrincipal();
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -55,8 +56,8 @@ public class SecurityUtils {
|
|||
Object principal = authentication.getPrincipal();
|
||||
if(principal!=null && !"anonymousUser".equals(principal.toString())){
|
||||
|
||||
User user = (User) principal;
|
||||
return new LoginUser(user, LocalDateTime.now());
|
||||
return (LoginUser) principal;
|
||||
// return new LoginUser(user, LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
|
@ -15,7 +16,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
@ -27,6 +30,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.genersoft.iot.vmp.service.IUserService;
|
||||
/**
|
||||
* 配置Spring Security
|
||||
*
|
||||
|
@ -42,6 +46,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
@Autowired
|
||||
private IUserService userService;
|
||||
|
||||
@Autowired
|
||||
private DefaultUserDetailsServiceImpl userDetailsService;
|
||||
|
@ -101,12 +107,13 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
provider.setUserDetailsService(userDetailsService);
|
||||
// 设置密码加密算法
|
||||
provider.setPasswordEncoder(passwordEncoder());
|
||||
|
||||
auth.authenticationProvider(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.headers().contentTypeOptions().disable()
|
||||
http.headers().contentTypeOptions().disable()
|
||||
.and().cors().configurationSource(configurationSource())
|
||||
.and().csrf().disable()
|
||||
.sessionManagement()
|
||||
|
@ -127,6 +134,41 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
.logoutSuccessHandler(logoutHandler)
|
||||
;
|
||||
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
|
||||
// .authorizeRequests();
|
||||
// //允许跨域请求的OPTIONS请求
|
||||
// registry.antMatchers(HttpMethod.OPTIONS)
|
||||
// .permitAll();
|
||||
// registry.and()
|
||||
// .authorizeRequests()
|
||||
// .antMatchers(HttpMethod.GET,
|
||||
// "/",
|
||||
// "/swagger-ui/",
|
||||
// "/doc.html")
|
||||
// .permitAll()
|
||||
// .antMatchers("/api/user/login", "/api/ptz/**", "/zlm/**", "/api/server/**","/index/hook/**","/index/hook/abl/**", "/swagger-ui/**", "/doc.html#/**")
|
||||
// .permitAll()
|
||||
// .anyRequest()
|
||||
// .authenticated()
|
||||
// // 关闭跨站请求防护及不使用session
|
||||
// .and()
|
||||
// .headers().contentTypeOptions().disable()
|
||||
// .and()
|
||||
// .cors().configurationSource(configurationSource())
|
||||
// .and()
|
||||
// .csrf()
|
||||
// .disable()
|
||||
// .sessionManagement()
|
||||
// .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
// // 自定义权限拒绝处理类
|
||||
// .and()
|
||||
// .exceptionHandling()
|
||||
// .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
|
||||
// .and().logout().logoutUrl("/api/user/logout").permitAll()
|
||||
// .logoutSuccessHandler(logoutHandler)
|
||||
// // 自定义权限拦截器JWT过滤器
|
||||
// .and()
|
||||
// .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,11 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
|||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
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.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
@ -25,6 +30,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
import javax.security.sasl.AuthenticationException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -42,6 +48,12 @@ public class UserController {
|
|||
@Autowired
|
||||
private IRoleService roleService;
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
public UserController(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "登录", description = "登录成功后返回AccessToken, 可以从返回值获取到也可以从响应头中获取到," +
|
||||
|
@ -51,18 +63,16 @@ public class UserController {
|
|||
@Parameter(name = "password", description = "密码(32位md5加密)", required = true)
|
||||
public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){
|
||||
LoginUser user;
|
||||
try {
|
||||
user = SecurityUtils.login(username, password, authenticationManager);
|
||||
} catch (AuthenticationException e) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
|
||||
}
|
||||
if (user == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误");
|
||||
}else {
|
||||
String jwt = JwtUtils.createToken(username);
|
||||
response.setHeader(JwtUtils.getHeader(), jwt);
|
||||
user.setAccessToken(jwt);
|
||||
}
|
||||
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
|
||||
user = (LoginUser) userDetails;
|
||||
String jwt = JwtUtils.createToken(username);
|
||||
response.setHeader(JwtUtils.getHeader(), jwt);
|
||||
user.setAccessToken(jwt);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -210,14 +220,21 @@ public class UserController {
|
|||
|
||||
@PostMapping("/userInfo")
|
||||
@Operation(summary = "管理员修改普通用户密码")
|
||||
public LoginUser getUserInfo() {
|
||||
public LoginUser getUserInfo(Principal principal) {
|
||||
if (principal == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户不存在");
|
||||
}
|
||||
|
||||
// 获取当前登录用户id
|
||||
LoginUser userInfo = SecurityUtils.getUserInfo();
|
||||
|
||||
if (userInfo == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100);
|
||||
}
|
||||
User user = userService.getUser(userInfo.getUsername(), userInfo.getPassword());
|
||||
return new LoginUser(user, LocalDateTime.now());
|
||||
// User user = userService.getUser(userInfo.getUsername(), userInfo.getPassword());
|
||||
// if (user == null) {
|
||||
// throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户不存在");
|
||||
// }
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue