From 95688e400b6974953505c159f8fbf9d65784c724 Mon Sep 17 00:00:00 2001 From: 648540858 <648540858@qq.com> Date: Mon, 13 Mar 2023 09:45:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=BF=94=E5=9B=9Etoken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 15 +- .../AnonymousAuthenticationEntryPoint.java | 16 +- .../DefaultUserDetailsServiceImpl.java | 15 +- .../conf/security/InvalidSessionHandler.java | 24 --- .../security/JwtAuthenticationFilter.java | 65 +++++++++ .../iot/vmp/conf/security/JwtUtils.java | 138 ++++++++++++++++++ .../conf/security/LoginSuccessHandler.java | 13 +- .../iot/vmp/conf/security/SecurityUtils.java | 25 +++- .../vmp/conf/security/WebSecurityConfig.java | 95 +++++++----- .../iot/vmp/conf/security/dto/JwtUser.java | 53 +++++++ .../iot/vmp/vmanager/user/UserController.java | 8 +- 11 files changed, 378 insertions(+), 89 deletions(-) delete mode 100644 src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java create mode 100644 src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java create mode 100644 src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java diff --git a/pom.xml b/pom.xml index bcc3c36a..a675c6ff 100644 --- a/pom.xml +++ b/pom.xml @@ -216,8 +216,6 @@ 4.10.0 - - io.github.rburgst @@ -226,10 +224,17 @@ + + + + + + + - net.sf.kxml - kxml2 - 2.3.0 + org.bitbucket.b_c + jose4j + 0.9.3 diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java index 35c68d5d..7a178d9e 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java @@ -1,10 +1,11 @@ package com.genersoft.iot.vmp.conf.security; import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @@ -17,12 +18,17 @@ import java.io.IOException; * @author lin */ @Component -public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { - - private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class); +public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { + System.err.println(e.getMessage()); + String jwt = request.getHeader(JwtUtils.getHeader()); + JwtUser jwtUser = JwtUtils.verifyToken(jwt); + String username = jwtUser.getUserName(); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() ); + SecurityContextHolder.getContext().setAuthentication(token); + System.out.println(jwt); // 允许跨域 String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Credentials", "true"); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java index 509a1e03..0cda4a5c 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java @@ -1,7 +1,9 @@ package com.genersoft.iot.vmp.conf.security; -import java.time.LocalDateTime; - +import com.alibaba.excel.util.StringUtils; +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.service.IUserService; +import com.genersoft.iot.vmp.storager.dao.dto.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -10,10 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; -import com.alibaba.excel.util.StringUtils; -import com.genersoft.iot.vmp.conf.security.dto.LoginUser; -import com.genersoft.iot.vmp.service.IUserService; -import com.genersoft.iot.vmp.storager.dao.dto.User; +import java.time.LocalDateTime; /** * 用户登录认证逻辑 @@ -45,4 +44,8 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService { } + + + + } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java deleted file mode 100644 index f3fd0685..00000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.genersoft.iot.vmp.conf.security; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.web.session.InvalidSessionStrategy; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 登录超时的处理 - */ -public class InvalidSessionHandler implements InvalidSessionStrategy { - - private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class); - - @Override - public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException { - String username = request.getParameter("username"); - logger.info("[登录超时] - [{}]", username); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java new file mode 100644 index 00000000..91709aa7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java @@ -0,0 +1,65 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +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; +import java.util.ArrayList; + +/** + * jwt token 过滤器 + */ + +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + String jwt = request.getHeader(JwtUtils.getHeader()); + // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的 + // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口 + if (StringUtils.isBlank(jwt)) { + chain.doFilter(request, response); + return; + } + + + 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: + } + +// String password = SecurityUtils.encryptPassword(jwtUser.getPassword()); +// user.setPassword(password); + + // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() ); + SecurityContextHolder.getContext().setAuthentication(token); + chain.doFilter(request, response); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java new file mode 100644 index 00000000..378e5d64 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java @@ -0,0 +1,138 @@ +package com.genersoft.iot.vmp.conf.security; + +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; +import org.jose4j.json.JsonUtil; +import org.jose4j.jwk.RsaJsonWebKey; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.NumericDate; +import org.jose4j.jwt.consumer.ErrorCodes; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.PrivateKey; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +public class JwtUtils { + + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); + + private static final String HEADER = "Access-Token"; + private static final String AUDIENCE = "Audience"; + + private static final long EXPIRED_THRESHOLD = 10 * 60; + + private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae"; + private static final String privateKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\",\"d\":\"ed7U_k3rJ4yTk70JtRSIfjKGiEb67BO1TabcymnljKO7RU8nage84zZYuSu_XpQsHk6P1f0Gzxkicghm_Er-FrfVn2pp70Xu52z3yRd6BJUgWLDFk97ngScIyw5OiULKU9SrZk2frDpftNCSUcIgb50F8m0QAnBa_CdPsQKbuuhLv8V8tBAV7F_lAwvSBgu56wRo3hPz5dWH8YeXM7XBfQ9viFMNEKd21sP_j5C7ueUnXT66nBxe3ZJEU3iuMYM6D6dB_KW2GfZC6WmTgvGhhxJD0h7aYmfjkD99MDleB7SkpbvoODOqiQ5Epb7Nyh6kv5u4KUv2CJYtATLZkUeMkQ\",\"p\":\"uBUjWPWtlGksmOqsqCNWksfqJvMcnP_8TDYN7e4-WnHL4N-9HjRuPDnp6kHvCIEi9SEfxm7gNxlRcWegvNQr3IZCz7TnCTexXc5NOklB9OavWFla6u-s3Thn6Tz45-EUjpJr0VJMxhO-KxGmuTwUXBBp4vN6K2qV6rQNFmgkWzk\",\"q\":\"tW_i7cCec56bHkhITL_79dXHz_PLC_f7xlynmlZJGU_d6mqOKmLBNBbTMLnYW8uAFiFzWxDeDHh1o5uF0mSQR-Z1Fg35OftnpbWpy0Cbc2la5WgXQjOwtG1eLYIY2BD3-wQ1VYDBCvowr4FDi-sngxwLqvwmrJ0xjhi99O-Gzcs\",\"dp\":\"q1d5jE85Hz_6M-eTh_lEluEf0NtPEc-vvhw-QO4V-cecNpbrCBdTWBmr4dE3NdpFeJc5ZVFEv-SACyei1MBEh0ItI_pFZi4BmMfy2ELh8ptaMMkTOESYyVy8U7veDq9RnBcr5i1Nqr0rsBkA77-9T6gzdvycBZdzLYAkAmwzEvk\",\"dq\":\"q29A2K08Crs-jmp2Bi8Q_8QzvIX6wSBbwZ4ir24AO-5_HNP56IrPS0yV2GCB0pqCOGb6_Hz_koDvhtuYoqdqvMVAtMoXR3YJBUaVXPt65p4RyNmFwIPe31zHs_BNUTsXVRMw4c16mci03-Af1sEm4HdLfxAp6sfM3xr5wcnhcek\",\"qi\":\"rHPgVTyHUHuYzcxfouyBfb1XAY8nshwn0ddo81o1BccD4Z7zo5It6SefDHjxCAbcmbiCcXBSooLcY-NF5FMv3fg19UE21VyLQltHcVjRRp2tRs4OHcM8yaXIU2x6N6Z6BP2tOksHb9MOBY1wAQzFOAKg_G4Sxev6-_6ud6RISuc\"}"; + private static final String publicKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\"}"; + + /** + * token过期时间(分钟) + */ + public static final long expirationTime = 30; + + public static String createToken(String username, String password) { + try { + /** + * “iss” (issuer) 发行人 + * + * “sub” (subject) 主题 + * + * “aud” (audience) 接收方 用户 + * + * “exp” (expiration time) 到期时间 + * + * “nbf” (not before) 在此之前不可用 + * + * “iat” (issued at) jwt的签发时间 + */ + //Payload + JwtClaims claims = new JwtClaims(); + claims.setGeneratedJwtId(); + claims.setIssuedAtToNow(); + // 令牌将过期的时间 分钟 + claims.setExpirationTimeMinutesInTheFuture(expirationTime); + claims.setNotBeforeMinutesInThePast(0); + claims.setSubject("login"); + claims.setAudience(AUDIENCE); + //添加自定义参数,必须是字符串类型 + claims.setClaim("username", username); + claims.setClaim("password", password); + + //jws + JsonWebSignature jws = new JsonWebSignature(); + //签名算法RS256 + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); + jws.setKeyIdHeaderValue(keyId); + jws.setPayload(claims.toJson()); + + PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey(); + jws.setKey(privateKey); + + //get token + String idToken = jws.getCompactSerialization(); + return idToken; + } catch (JoseException e) { + logger.error("[Token生成失败]: {}", e.getMessage()); + } + + return null; + } + + public static String getHeader() { + return HEADER; + } + + + public static JwtUser verifyToken(String token) { + + JwtUser jwtUser = new JwtUser(); + + try { + JwtConsumer consumer = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setMaxFutureValidityInMinutes(5256000) + .setAllowedClockSkewInSeconds(30) + .setRequireSubject() + //.setExpectedIssuer("") + .setExpectedAudience(AUDIENCE) + .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey()) + .build(); + + JwtClaims claims = consumer.processToClaims(token); + NumericDate expirationTime = claims.getExpirationTime(); + // 判断是否即将过期, 默认剩余时间小于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); + } + + String username = (String) claims.getClaimValue("username"); + String password = (String) claims.getClaimValue("password"); + jwtUser.setUserName(username); + jwtUser.setPassword(password); + + return jwtUser; + } catch (InvalidJwtException e) { + if (e.hasErrorCode(ErrorCodes.EXPIRED)) { + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); + }else { + jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION); + } + return jwtUser; + }catch (Exception e) { + logger.error("[Token解析失败]: {}", e.getMessage()); + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); + return jwtUser; + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java index 2d7e8a1b..d26342ef 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java @@ -21,7 +21,16 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { - String username = request.getParameter("username"); - logger.info("[登录成功] - [{}]", username); +// String username = request.getParameter("username"); +// httpServletResponse.setContentType("application/json;charset=UTF-8"); +// // 生成JWT,并放置到请求头中 +// String jwt = JwtUtils.createToken(authentication.getName(), ); +// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt); +// ServletOutputStream outputStream = httpServletResponse.getOutputStream(); +// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8)); +// outputStream.flush(); +// outputStream.close(); + +// logger.info("[登录成功] - [{}]", username); } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java index fd29d112..76f11620 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.conf.security; import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.storager.dao.dto.User; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -9,6 +10,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import javax.security.sasl.AuthenticationException; +import java.time.LocalDateTime; public class SecurityUtils { @@ -25,10 +27,16 @@ public class SecurityUtils { public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { //使用security框架自带的验证token生成器 也可以自定义。 UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password); - Authentication authenticate = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(authenticate); - LoginUser user = (LoginUser) authenticate.getPrincipal(); - return user; +// Authentication authenticate = authenticationManager.authenticate(token); +// SecurityContextHolder.getContext().setAuthentication(authenticate); + SecurityContextHolder.getContext().setAuthentication(token); + + +// LoginUser user = (LoginUser) authenticate.getPrincipal(); + User user = new User(); + user.setUsername(username); + LoginUser loginUser = new LoginUser(user, LocalDateTime.now()); + return loginUser; } /** @@ -49,8 +57,13 @@ public class SecurityUtils { if(authentication!=null){ Object principal = authentication.getPrincipal(); if(principal!=null && !"anonymousUser".equals(principal)){ - LoginUser user = (LoginUser) authentication.getPrincipal(); - return user; +// LoginUser user = (LoginUser) authentication.getPrincipal(); + + String username = (String) principal; + User user = new User(); + user.setUsername(username); + LoginUser loginUser = new LoginUser(user, LocalDateTime.now()); + return loginUser; } } return null; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index cce0d11c..c700b8c1 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -15,7 +15,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.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.util.List; @@ -56,22 +58,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { */ @Autowired private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint; -// /** -// * 超时处理 -// */ -// @Autowired -// private InvalidSessionHandler invalidSessionHandler; + @Autowired + private JwtAuthenticationFilter jwtAuthenticationFilter; -// /** -// * 顶号处理 -// */ -// @Autowired -// private SessionInformationExpiredHandler sessionInformationExpiredHandler; -// /** -// * 登录用户没有权限访问资源 -// */ -// @Autowired -// private LoginUserAccessDeniedHandler accessDeniedHandler; +// @Bean +// JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { +// JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager()); +// return jwtAuthenticationFilter; +// } /** @@ -126,35 +120,56 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.cors().and().csrf().disable(); - // 设置允许添加静态文件 - http.headers().contentTypeOptions().disable(); - http.authorizeRequests() - // 放行接口 + http.headers().contentTypeOptions().disable() + .and().cors() + .and().csrf().disable() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + + // 配置拦截规则 + .and() + .authorizeRequests() .antMatchers("/api/user/login","/index/hook/**").permitAll() - // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() - // 异常处理(权限拒绝、登录失效等) - .and().exceptionHandling() - //匿名用户访问无权限资源时的异常处理 + // 异常处理器 + .and() + .exceptionHandling() .authenticationEntryPoint(anonymousAuthenticationEntryPoint) -// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源 - // 登入 允许所有用户 - .and().formLogin().permitAll() - //登录成功处理逻辑 - .successHandler(loginSuccessHandler) - //登录失败处理逻辑 - .failureHandler(loginFailureHandler) - // 登出 - .and().logout().logoutUrl("/api/user/logout").permitAll() - //登出成功处理逻辑 - .logoutSuccessHandler(logoutHandler) - .deleteCookies("JSESSIONID") - // 会话管理 -// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理 -// .maximumSessions(1)//同一账号同时登录最大用户数 -// .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理 +// .accessDeniedHandler(jwtAccessDeniedHandler) + // 配置自定义的过滤器 +// .and() +// .addFilter(jwtAuthenticationFilter) + // 验证码过滤器放在UsernamePassword过滤器之前 +// .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class) ; + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); +// // 设置允许添加静态文件 +// http.headers().contentTypeOptions().disable(); +// http.authorizeRequests() +// // 放行接口 +// .antMatchers("/api/user/login","/index/hook/**").permitAll() +// // 除上面外的所有请求全部需要鉴权认证 +// .anyRequest().authenticated() +// // 禁用session +// .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) +// // 异常处理(权限拒绝、登录失效等) +// .and().exceptionHandling() +// // 匿名用户访问无权限资源时的异常处理 +// .authenticationEntryPoint(anonymousAuthenticationEntryPoint) +// // 登录 允许所有用户 +// .and().formLogin() +// // 登录成功处理逻辑 在这里给出JWT +// .successHandler(loginSuccessHandler) +// // 登录失败处理逻辑 +// .failureHandler(loginFailureHandler) +// // 登出 +// .and().logout().logoutUrl("/api/user/logout").permitAll() +// // 登出成功处理逻辑 +// .logoutSuccessHandler(logoutHandler) +// // 配置自定义的过滤器 +// .and() +// .addFilter(jwtAuthenticationFilter()) +// ; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java new file mode 100644 index 00000000..1639d1fc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java @@ -0,0 +1,53 @@ +package com.genersoft.iot.vmp.conf.security.dto; + +public class JwtUser { + + public enum TokenStatus{ + /** + * 正常的使用状态 + */ + NORMAL, + /** + * 过期而失效 + */ + EXPIRED, + /** + * 即将过期 + */ + EXPIRING_SOON, + /** + * 异常 + */ + EXCEPTION + } + + private String userName; + + private String password; + + private TokenStatus status; + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public TokenStatus getStatus() { + return status; + } + + public void setStatus(TokenStatus status) { + this.status = status; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java index 127e83b8..826dd51e 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.vmanager.user; import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.conf.security.SecurityUtils; import com.genersoft.iot.vmp.conf.security.dto.LoginUser; import com.genersoft.iot.vmp.service.IRoleService; @@ -21,6 +22,8 @@ import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import javax.security.sasl.AuthenticationException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; @Tag(name = "用户管理") @@ -43,7 +46,7 @@ public class UserController { @Operation(summary = "登录") @Parameter(name = "username", description = "用户名", required = true) @Parameter(name = "password", description = "密码(32位md5加密)", required = true) - public LoginUser login(@RequestParam String username, @RequestParam String password){ + public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){ LoginUser user = null; try { user = SecurityUtils.login(username, password, authenticationManager); @@ -52,6 +55,9 @@ public class UserController { } if (user == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误"); + }else { + String jwt = JwtUtils.createToken(username, password); + response.setHeader(JwtUtils.getHeader(), jwt); } return user; }