Commit 95688e400b6974953505c159f8fbf9d65784c724
1 parent
ec90519c
初步实现登录返回token
Showing
11 changed files
with
379 additions
and
90 deletions
pom.xml
| ... | ... | @@ -216,8 +216,6 @@ |
| 216 | 216 | <version>4.10.0</version> |
| 217 | 217 | </dependency> |
| 218 | 218 | |
| 219 | - | |
| 220 | - | |
| 221 | 219 | <!-- okhttp-digest --> |
| 222 | 220 | <dependency> |
| 223 | 221 | <groupId>io.github.rburgst</groupId> |
| ... | ... | @@ -226,10 +224,17 @@ |
| 226 | 224 | </dependency> |
| 227 | 225 | |
| 228 | 226 | <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 --> |
| 227 | +<!-- <dependency>--> | |
| 228 | +<!-- <groupId>net.sf.kxml</groupId>--> | |
| 229 | +<!-- <artifactId>kxml2</artifactId>--> | |
| 230 | +<!-- <version>2.3.0</version>--> | |
| 231 | +<!-- </dependency>--> | |
| 232 | + | |
| 233 | + <!-- jwt实现 --> | |
| 229 | 234 | <dependency> |
| 230 | - <groupId>net.sf.kxml</groupId> | |
| 231 | - <artifactId>kxml2</artifactId> | |
| 232 | - <version>2.3.0</version> | |
| 235 | + <groupId>org.bitbucket.b_c</groupId> | |
| 236 | + <artifactId>jose4j</artifactId> | |
| 237 | + <version>0.9.3</version> | |
| 233 | 238 | </dependency> |
| 234 | 239 | |
| 235 | 240 | <!--反向代理--> | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
| 1 | 1 | package com.genersoft.iot.vmp.conf.security; |
| 2 | 2 | |
| 3 | 3 | import com.alibaba.fastjson2.JSONObject; |
| 4 | +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; | |
| 4 | 5 | import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; |
| 5 | -import org.slf4j.Logger; | |
| 6 | -import org.slf4j.LoggerFactory; | |
| 6 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |
| 7 | 7 | import org.springframework.security.core.AuthenticationException; |
| 8 | +import org.springframework.security.core.context.SecurityContextHolder; | |
| 8 | 9 | import org.springframework.security.web.AuthenticationEntryPoint; |
| 9 | 10 | import org.springframework.stereotype.Component; |
| 10 | 11 | |
| ... | ... | @@ -17,12 +18,17 @@ import java.io.IOException; |
| 17 | 18 | * @author lin |
| 18 | 19 | */ |
| 19 | 20 | @Component |
| 20 | -public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { | |
| 21 | - | |
| 22 | - private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class); | |
| 21 | +public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { | |
| 23 | 22 | |
| 24 | 23 | @Override |
| 25 | 24 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { |
| 25 | + System.err.println(e.getMessage()); | |
| 26 | + String jwt = request.getHeader(JwtUtils.getHeader()); | |
| 27 | + JwtUser jwtUser = JwtUtils.verifyToken(jwt); | |
| 28 | + String username = jwtUser.getUserName(); | |
| 29 | + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() ); | |
| 30 | + SecurityContextHolder.getContext().setAuthentication(token); | |
| 31 | + System.out.println(jwt); | |
| 26 | 32 | // 允许跨域 |
| 27 | 33 | String origin = request.getHeader("Origin"); |
| 28 | 34 | response.setHeader("Access-Control-Allow-Credentials", "true"); | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
| 1 | 1 | package com.genersoft.iot.vmp.conf.security; |
| 2 | 2 | |
| 3 | -import java.time.LocalDateTime; | |
| 4 | - | |
| 3 | +import com.alibaba.excel.util.StringUtils; | |
| 4 | +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; | |
| 5 | +import com.genersoft.iot.vmp.service.IUserService; | |
| 6 | +import com.genersoft.iot.vmp.storager.dao.dto.User; | |
| 5 | 7 | import org.slf4j.Logger; |
| 6 | 8 | import org.slf4j.LoggerFactory; |
| 7 | 9 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -10,10 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService; |
| 10 | 12 | import org.springframework.security.core.userdetails.UsernameNotFoundException; |
| 11 | 13 | import org.springframework.stereotype.Component; |
| 12 | 14 | |
| 13 | -import com.alibaba.excel.util.StringUtils; | |
| 14 | -import com.genersoft.iot.vmp.conf.security.dto.LoginUser; | |
| 15 | -import com.genersoft.iot.vmp.service.IUserService; | |
| 16 | -import com.genersoft.iot.vmp.storager.dao.dto.User; | |
| 15 | +import java.time.LocalDateTime; | |
| 17 | 16 | |
| 18 | 17 | /** |
| 19 | 18 | * 用户登录认证逻辑 |
| ... | ... | @@ -45,4 +44,8 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService { |
| 45 | 44 | } |
| 46 | 45 | |
| 47 | 46 | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 48 | 51 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.conf.security; | |
| 2 | - | |
| 3 | -import org.slf4j.Logger; | |
| 4 | -import org.slf4j.LoggerFactory; | |
| 5 | -import org.springframework.security.web.session.InvalidSessionStrategy; | |
| 6 | - | |
| 7 | -import javax.servlet.ServletException; | |
| 8 | -import javax.servlet.http.HttpServletRequest; | |
| 9 | -import javax.servlet.http.HttpServletResponse; | |
| 10 | -import java.io.IOException; | |
| 11 | - | |
| 12 | -/** | |
| 13 | - * 登录超时的处理 | |
| 14 | - */ | |
| 15 | -public class InvalidSessionHandler implements InvalidSessionStrategy { | |
| 16 | - | |
| 17 | - private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class); | |
| 18 | - | |
| 19 | - @Override | |
| 20 | - public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException { | |
| 21 | - String username = request.getParameter("username"); | |
| 22 | - logger.info("[登录超时] - [{}]", username); | |
| 23 | - } | |
| 24 | -} |
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.conf.security; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; | |
| 4 | +import org.apache.commons.lang3.StringUtils; | |
| 5 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |
| 6 | +import org.springframework.security.core.context.SecurityContextHolder; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | +import org.springframework.web.filter.OncePerRequestFilter; | |
| 9 | + | |
| 10 | +import javax.servlet.FilterChain; | |
| 11 | +import javax.servlet.ServletException; | |
| 12 | +import javax.servlet.http.HttpServletRequest; | |
| 13 | +import javax.servlet.http.HttpServletResponse; | |
| 14 | +import java.io.IOException; | |
| 15 | +import java.util.ArrayList; | |
| 16 | + | |
| 17 | +/** | |
| 18 | + * jwt token 过滤器 | |
| 19 | + */ | |
| 20 | + | |
| 21 | +@Component | |
| 22 | +public class JwtAuthenticationFilter extends OncePerRequestFilter { | |
| 23 | + | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { | |
| 27 | + String jwt = request.getHeader(JwtUtils.getHeader()); | |
| 28 | + // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的 | |
| 29 | + // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口 | |
| 30 | + if (StringUtils.isBlank(jwt)) { | |
| 31 | + chain.doFilter(request, response); | |
| 32 | + return; | |
| 33 | + } | |
| 34 | + | |
| 35 | + | |
| 36 | + JwtUser jwtUser = JwtUtils.verifyToken(jwt); | |
| 37 | + String username = jwtUser.getUserName(); | |
| 38 | + // TODO 处理各个状态 | |
| 39 | + switch (jwtUser.getStatus()){ | |
| 40 | + case EXPIRED: | |
| 41 | + response.setStatus(400); | |
| 42 | + chain.doFilter(request, response); | |
| 43 | + // 异常 | |
| 44 | + return; | |
| 45 | + case EXCEPTION: | |
| 46 | + // 过期 | |
| 47 | + response.setStatus(400); | |
| 48 | + chain.doFilter(request, response); | |
| 49 | + return; | |
| 50 | + case EXPIRING_SOON: | |
| 51 | + // 即将过期 | |
| 52 | +// return; | |
| 53 | + default: | |
| 54 | + } | |
| 55 | + | |
| 56 | +// String password = SecurityUtils.encryptPassword(jwtUser.getPassword()); | |
| 57 | +// user.setPassword(password); | |
| 58 | + | |
| 59 | + // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录 | |
| 60 | + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() ); | |
| 61 | + SecurityContextHolder.getContext().setAuthentication(token); | |
| 62 | + chain.doFilter(request, response); | |
| 63 | + } | |
| 64 | + | |
| 65 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.conf.security; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.conf.security.dto.JwtUser; | |
| 4 | +import org.jose4j.json.JsonUtil; | |
| 5 | +import org.jose4j.jwk.RsaJsonWebKey; | |
| 6 | +import org.jose4j.jws.AlgorithmIdentifiers; | |
| 7 | +import org.jose4j.jws.JsonWebSignature; | |
| 8 | +import org.jose4j.jwt.JwtClaims; | |
| 9 | +import org.jose4j.jwt.NumericDate; | |
| 10 | +import org.jose4j.jwt.consumer.ErrorCodes; | |
| 11 | +import org.jose4j.jwt.consumer.InvalidJwtException; | |
| 12 | +import org.jose4j.jwt.consumer.JwtConsumer; | |
| 13 | +import org.jose4j.jwt.consumer.JwtConsumerBuilder; | |
| 14 | +import org.jose4j.lang.JoseException; | |
| 15 | +import org.slf4j.Logger; | |
| 16 | +import org.slf4j.LoggerFactory; | |
| 17 | + | |
| 18 | +import java.security.PrivateKey; | |
| 19 | +import java.time.LocalDateTime; | |
| 20 | +import java.time.ZoneOffset; | |
| 21 | + | |
| 22 | +public class JwtUtils { | |
| 23 | + | |
| 24 | + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); | |
| 25 | + | |
| 26 | + private static final String HEADER = "Access-Token"; | |
| 27 | + private static final String AUDIENCE = "Audience"; | |
| 28 | + | |
| 29 | + private static final long EXPIRED_THRESHOLD = 10 * 60; | |
| 30 | + | |
| 31 | + private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae"; | |
| 32 | + 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\"}"; | |
| 33 | + 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\"}"; | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * token过期时间(分钟) | |
| 37 | + */ | |
| 38 | + public static final long expirationTime = 30; | |
| 39 | + | |
| 40 | + public static String createToken(String username, String password) { | |
| 41 | + try { | |
| 42 | + /** | |
| 43 | + * “iss” (issuer) 发行人 | |
| 44 | + * | |
| 45 | + * “sub” (subject) 主题 | |
| 46 | + * | |
| 47 | + * “aud” (audience) 接收方 用户 | |
| 48 | + * | |
| 49 | + * “exp” (expiration time) 到期时间 | |
| 50 | + * | |
| 51 | + * “nbf” (not before) 在此之前不可用 | |
| 52 | + * | |
| 53 | + * “iat” (issued at) jwt的签发时间 | |
| 54 | + */ | |
| 55 | + //Payload | |
| 56 | + JwtClaims claims = new JwtClaims(); | |
| 57 | + claims.setGeneratedJwtId(); | |
| 58 | + claims.setIssuedAtToNow(); | |
| 59 | + // 令牌将过期的时间 分钟 | |
| 60 | + claims.setExpirationTimeMinutesInTheFuture(expirationTime); | |
| 61 | + claims.setNotBeforeMinutesInThePast(0); | |
| 62 | + claims.setSubject("login"); | |
| 63 | + claims.setAudience(AUDIENCE); | |
| 64 | + //添加自定义参数,必须是字符串类型 | |
| 65 | + claims.setClaim("username", username); | |
| 66 | + claims.setClaim("password", password); | |
| 67 | + | |
| 68 | + //jws | |
| 69 | + JsonWebSignature jws = new JsonWebSignature(); | |
| 70 | + //签名算法RS256 | |
| 71 | + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); | |
| 72 | + jws.setKeyIdHeaderValue(keyId); | |
| 73 | + jws.setPayload(claims.toJson()); | |
| 74 | + | |
| 75 | + PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey(); | |
| 76 | + jws.setKey(privateKey); | |
| 77 | + | |
| 78 | + //get token | |
| 79 | + String idToken = jws.getCompactSerialization(); | |
| 80 | + return idToken; | |
| 81 | + } catch (JoseException e) { | |
| 82 | + logger.error("[Token生成失败]: {}", e.getMessage()); | |
| 83 | + } | |
| 84 | + | |
| 85 | + return null; | |
| 86 | + } | |
| 87 | + | |
| 88 | + public static String getHeader() { | |
| 89 | + return HEADER; | |
| 90 | + } | |
| 91 | + | |
| 92 | + | |
| 93 | + public static JwtUser verifyToken(String token) { | |
| 94 | + | |
| 95 | + JwtUser jwtUser = new JwtUser(); | |
| 96 | + | |
| 97 | + try { | |
| 98 | + JwtConsumer consumer = new JwtConsumerBuilder() | |
| 99 | + .setRequireExpirationTime() | |
| 100 | + .setMaxFutureValidityInMinutes(5256000) | |
| 101 | + .setAllowedClockSkewInSeconds(30) | |
| 102 | + .setRequireSubject() | |
| 103 | + //.setExpectedIssuer("") | |
| 104 | + .setExpectedAudience(AUDIENCE) | |
| 105 | + .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey()) | |
| 106 | + .build(); | |
| 107 | + | |
| 108 | + JwtClaims claims = consumer.processToClaims(token); | |
| 109 | + NumericDate expirationTime = claims.getExpirationTime(); | |
| 110 | + // 判断是否即将过期, 默认剩余时间小于5分钟未即将过期 | |
| 111 | + // 剩余时间 (秒) | |
| 112 | + long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue(); | |
| 113 | + if (timeRemaining < 5 * 60) { | |
| 114 | + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON); | |
| 115 | + }else { | |
| 116 | + jwtUser.setStatus(JwtUser.TokenStatus.NORMAL); | |
| 117 | + } | |
| 118 | + | |
| 119 | + String username = (String) claims.getClaimValue("username"); | |
| 120 | + String password = (String) claims.getClaimValue("password"); | |
| 121 | + jwtUser.setUserName(username); | |
| 122 | + jwtUser.setPassword(password); | |
| 123 | + | |
| 124 | + return jwtUser; | |
| 125 | + } catch (InvalidJwtException e) { | |
| 126 | + if (e.hasErrorCode(ErrorCodes.EXPIRED)) { | |
| 127 | + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); | |
| 128 | + }else { | |
| 129 | + jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION); | |
| 130 | + } | |
| 131 | + return jwtUser; | |
| 132 | + }catch (Exception e) { | |
| 133 | + logger.error("[Token解析失败]: {}", e.getMessage()); | |
| 134 | + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); | |
| 135 | + return jwtUser; | |
| 136 | + } | |
| 137 | + } | |
| 138 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
| ... | ... | @@ -21,7 +21,16 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler { |
| 21 | 21 | |
| 22 | 22 | @Override |
| 23 | 23 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { |
| 24 | - String username = request.getParameter("username"); | |
| 25 | - logger.info("[登录成功] - [{}]", username); | |
| 24 | +// String username = request.getParameter("username"); | |
| 25 | +// httpServletResponse.setContentType("application/json;charset=UTF-8"); | |
| 26 | +// // 生成JWT,并放置到请求头中 | |
| 27 | +// String jwt = JwtUtils.createToken(authentication.getName(), ); | |
| 28 | +// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt); | |
| 29 | +// ServletOutputStream outputStream = httpServletResponse.getOutputStream(); | |
| 30 | +// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8)); | |
| 31 | +// outputStream.flush(); | |
| 32 | +// outputStream.close(); | |
| 33 | + | |
| 34 | +// logger.info("[登录成功] - [{}]", username); | |
| 26 | 35 | } |
| 27 | 36 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
| 1 | 1 | package com.genersoft.iot.vmp.conf.security; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.conf.security.dto.LoginUser; |
| 4 | +import com.genersoft.iot.vmp.storager.dao.dto.User; | |
| 4 | 5 | import org.springframework.security.authentication.AuthenticationManager; |
| 5 | 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
| 6 | 7 | import org.springframework.security.core.Authentication; |
| ... | ... | @@ -9,6 +10,7 @@ import org.springframework.security.core.context.SecurityContextHolder; |
| 9 | 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| 10 | 11 | |
| 11 | 12 | import javax.security.sasl.AuthenticationException; |
| 13 | +import java.time.LocalDateTime; | |
| 12 | 14 | |
| 13 | 15 | public class SecurityUtils { |
| 14 | 16 | |
| ... | ... | @@ -25,10 +27,16 @@ public class SecurityUtils { |
| 25 | 27 | public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { |
| 26 | 28 | //使用security框架自带的验证token生成器 也可以自定义。 |
| 27 | 29 | UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password); |
| 28 | - Authentication authenticate = authenticationManager.authenticate(token); | |
| 29 | - SecurityContextHolder.getContext().setAuthentication(authenticate); | |
| 30 | - LoginUser user = (LoginUser) authenticate.getPrincipal(); | |
| 31 | - return user; | |
| 30 | +// Authentication authenticate = authenticationManager.authenticate(token); | |
| 31 | +// SecurityContextHolder.getContext().setAuthentication(authenticate); | |
| 32 | + SecurityContextHolder.getContext().setAuthentication(token); | |
| 33 | + | |
| 34 | + | |
| 35 | +// LoginUser user = (LoginUser) authenticate.getPrincipal(); | |
| 36 | + User user = new User(); | |
| 37 | + user.setUsername(username); | |
| 38 | + LoginUser loginUser = new LoginUser(user, LocalDateTime.now()); | |
| 39 | + return loginUser; | |
| 32 | 40 | } |
| 33 | 41 | |
| 34 | 42 | /** |
| ... | ... | @@ -49,8 +57,13 @@ public class SecurityUtils { |
| 49 | 57 | if(authentication!=null){ |
| 50 | 58 | Object principal = authentication.getPrincipal(); |
| 51 | 59 | if(principal!=null && !"anonymousUser".equals(principal)){ |
| 52 | - LoginUser user = (LoginUser) authentication.getPrincipal(); | |
| 53 | - return user; | |
| 60 | +// LoginUser user = (LoginUser) authentication.getPrincipal(); | |
| 61 | + | |
| 62 | + String username = (String) principal; | |
| 63 | + User user = new User(); | |
| 64 | + user.setUsername(username); | |
| 65 | + LoginUser loginUser = new LoginUser(user, LocalDateTime.now()); | |
| 66 | + return loginUser; | |
| 54 | 67 | } |
| 55 | 68 | } |
| 56 | 69 | return null; | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
| ... | ... | @@ -15,7 +15,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
| 15 | 15 | import org.springframework.security.config.annotation.web.builders.WebSecurity; |
| 16 | 16 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
| 17 | 17 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
| 18 | +import org.springframework.security.config.http.SessionCreationPolicy; | |
| 18 | 19 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| 20 | +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | |
| 19 | 21 | |
| 20 | 22 | import java.util.List; |
| 21 | 23 | |
| ... | ... | @@ -56,22 +58,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { |
| 56 | 58 | */ |
| 57 | 59 | @Autowired |
| 58 | 60 | private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint; |
| 59 | -// /** | |
| 60 | -// * 超时处理 | |
| 61 | -// */ | |
| 62 | -// @Autowired | |
| 63 | -// private InvalidSessionHandler invalidSessionHandler; | |
| 64 | - | |
| 65 | -// /** | |
| 66 | -// * 顶号处理 | |
| 67 | -// */ | |
| 68 | -// @Autowired | |
| 69 | -// private SessionInformationExpiredHandler sessionInformationExpiredHandler; | |
| 70 | -// /** | |
| 71 | -// * 登录用户没有权限访问资源 | |
| 72 | -// */ | |
| 73 | -// @Autowired | |
| 74 | -// private LoginUserAccessDeniedHandler accessDeniedHandler; | |
| 61 | + @Autowired | |
| 62 | + private JwtAuthenticationFilter jwtAuthenticationFilter; | |
| 63 | + | |
| 64 | +// @Bean | |
| 65 | +// JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { | |
| 66 | +// JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager()); | |
| 67 | +// return jwtAuthenticationFilter; | |
| 68 | +// } | |
| 75 | 69 | |
| 76 | 70 | |
| 77 | 71 | /** |
| ... | ... | @@ -126,35 +120,56 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { |
| 126 | 120 | |
| 127 | 121 | @Override |
| 128 | 122 | protected void configure(HttpSecurity http) throws Exception { |
| 129 | - http.cors().and().csrf().disable(); | |
| 130 | - // 设置允许添加静态文件 | |
| 131 | - http.headers().contentTypeOptions().disable(); | |
| 132 | - http.authorizeRequests() | |
| 133 | - // 放行接口 | |
| 123 | + http.headers().contentTypeOptions().disable() | |
| 124 | + .and().cors() | |
| 125 | + .and().csrf().disable() | |
| 126 | + .sessionManagement() | |
| 127 | + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) | |
| 128 | + | |
| 129 | + // 配置拦截规则 | |
| 130 | + .and() | |
| 131 | + .authorizeRequests() | |
| 134 | 132 | .antMatchers("/api/user/login","/index/hook/**").permitAll() |
| 135 | - // 除上面外的所有请求全部需要鉴权认证 | |
| 136 | 133 | .anyRequest().authenticated() |
| 137 | - // 异常处理(权限拒绝、登录失效等) | |
| 138 | - .and().exceptionHandling() | |
| 139 | - //匿名用户访问无权限资源时的异常处理 | |
| 134 | + // 异常处理器 | |
| 135 | + .and() | |
| 136 | + .exceptionHandling() | |
| 140 | 137 | .authenticationEntryPoint(anonymousAuthenticationEntryPoint) |
| 141 | -// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源 | |
| 142 | - // 登入 允许所有用户 | |
| 143 | - .and().formLogin().permitAll() | |
| 144 | - //登录成功处理逻辑 | |
| 145 | - .successHandler(loginSuccessHandler) | |
| 146 | - //登录失败处理逻辑 | |
| 147 | - .failureHandler(loginFailureHandler) | |
| 148 | - // 登出 | |
| 149 | - .and().logout().logoutUrl("/api/user/logout").permitAll() | |
| 150 | - //登出成功处理逻辑 | |
| 151 | - .logoutSuccessHandler(logoutHandler) | |
| 152 | - .deleteCookies("JSESSIONID") | |
| 153 | - // 会话管理 | |
| 154 | -// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理 | |
| 155 | -// .maximumSessions(1)//同一账号同时登录最大用户数 | |
| 156 | -// .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理 | |
| 138 | +// .accessDeniedHandler(jwtAccessDeniedHandler) | |
| 139 | + // 配置自定义的过滤器 | |
| 140 | +// .and() | |
| 141 | +// .addFilter(jwtAuthenticationFilter) | |
| 142 | + // 验证码过滤器放在UsernamePassword过滤器之前 | |
| 143 | +// .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class) | |
| 157 | 144 | ; |
| 145 | + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | |
| 146 | +// // 设置允许添加静态文件 | |
| 147 | +// http.headers().contentTypeOptions().disable(); | |
| 148 | +// http.authorizeRequests() | |
| 149 | +// // 放行接口 | |
| 150 | +// .antMatchers("/api/user/login","/index/hook/**").permitAll() | |
| 151 | +// // 除上面外的所有请求全部需要鉴权认证 | |
| 152 | +// .anyRequest().authenticated() | |
| 153 | +// // 禁用session | |
| 154 | +// .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) | |
| 155 | +// // 异常处理(权限拒绝、登录失效等) | |
| 156 | +// .and().exceptionHandling() | |
| 157 | +// // 匿名用户访问无权限资源时的异常处理 | |
| 158 | +// .authenticationEntryPoint(anonymousAuthenticationEntryPoint) | |
| 159 | +// // 登录 允许所有用户 | |
| 160 | +// .and().formLogin() | |
| 161 | +// // 登录成功处理逻辑 在这里给出JWT | |
| 162 | +// .successHandler(loginSuccessHandler) | |
| 163 | +// // 登录失败处理逻辑 | |
| 164 | +// .failureHandler(loginFailureHandler) | |
| 165 | +// // 登出 | |
| 166 | +// .and().logout().logoutUrl("/api/user/logout").permitAll() | |
| 167 | +// // 登出成功处理逻辑 | |
| 168 | +// .logoutSuccessHandler(logoutHandler) | |
| 169 | +// // 配置自定义的过滤器 | |
| 170 | +// .and() | |
| 171 | +// .addFilter(jwtAuthenticationFilter()) | |
| 172 | +// ; | |
| 158 | 173 | |
| 159 | 174 | } |
| 160 | 175 | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.conf.security.dto; | |
| 2 | + | |
| 3 | +public class JwtUser { | |
| 4 | + | |
| 5 | + public enum TokenStatus{ | |
| 6 | + /** | |
| 7 | + * 正常的使用状态 | |
| 8 | + */ | |
| 9 | + NORMAL, | |
| 10 | + /** | |
| 11 | + * 过期而失效 | |
| 12 | + */ | |
| 13 | + EXPIRED, | |
| 14 | + /** | |
| 15 | + * 即将过期 | |
| 16 | + */ | |
| 17 | + EXPIRING_SOON, | |
| 18 | + /** | |
| 19 | + * 异常 | |
| 20 | + */ | |
| 21 | + EXCEPTION | |
| 22 | + } | |
| 23 | + | |
| 24 | + private String userName; | |
| 25 | + | |
| 26 | + private String password; | |
| 27 | + | |
| 28 | + private TokenStatus status; | |
| 29 | + | |
| 30 | + public String getUserName() { | |
| 31 | + return userName; | |
| 32 | + } | |
| 33 | + | |
| 34 | + public void setUserName(String userName) { | |
| 35 | + this.userName = userName; | |
| 36 | + } | |
| 37 | + | |
| 38 | + public TokenStatus getStatus() { | |
| 39 | + return status; | |
| 40 | + } | |
| 41 | + | |
| 42 | + public void setStatus(TokenStatus status) { | |
| 43 | + this.status = status; | |
| 44 | + } | |
| 45 | + | |
| 46 | + public String getPassword() { | |
| 47 | + return password; | |
| 48 | + } | |
| 49 | + | |
| 50 | + public void setPassword(String password) { | |
| 51 | + this.password = password; | |
| 52 | + } | |
| 53 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
| 1 | 1 | package com.genersoft.iot.vmp.vmanager.user; |
| 2 | 2 | |
| 3 | 3 | import com.genersoft.iot.vmp.conf.exception.ControllerException; |
| 4 | +import com.genersoft.iot.vmp.conf.security.JwtUtils; | |
| 4 | 5 | import com.genersoft.iot.vmp.conf.security.SecurityUtils; |
| 5 | 6 | import com.genersoft.iot.vmp.conf.security.dto.LoginUser; |
| 6 | 7 | import com.genersoft.iot.vmp.service.IRoleService; |
| ... | ... | @@ -21,6 +22,8 @@ import org.springframework.util.ObjectUtils; |
| 21 | 22 | import org.springframework.web.bind.annotation.*; |
| 22 | 23 | |
| 23 | 24 | import javax.security.sasl.AuthenticationException; |
| 25 | +import javax.servlet.http.HttpServletRequest; | |
| 26 | +import javax.servlet.http.HttpServletResponse; | |
| 24 | 27 | import java.util.List; |
| 25 | 28 | |
| 26 | 29 | @Tag(name = "用户管理") |
| ... | ... | @@ -43,7 +46,7 @@ public class UserController { |
| 43 | 46 | @Operation(summary = "登录") |
| 44 | 47 | @Parameter(name = "username", description = "用户名", required = true) |
| 45 | 48 | @Parameter(name = "password", description = "密码(32位md5加密)", required = true) |
| 46 | - public LoginUser login(@RequestParam String username, @RequestParam String password){ | |
| 49 | + public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){ | |
| 47 | 50 | LoginUser user = null; |
| 48 | 51 | try { |
| 49 | 52 | user = SecurityUtils.login(username, password, authenticationManager); |
| ... | ... | @@ -52,6 +55,9 @@ public class UserController { |
| 52 | 55 | } |
| 53 | 56 | if (user == null) { |
| 54 | 57 | throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误"); |
| 58 | + }else { | |
| 59 | + String jwt = JwtUtils.createToken(username, password); | |
| 60 | + response.setHeader(JwtUtils.getHeader(), jwt); | |
| 55 | 61 | } |
| 56 | 62 | return user; |
| 57 | 63 | } | ... | ... |