ylbtech-Java-Class-@I:java.annotation.Resource |
1.返回頂部 |
2.返回頂部 |
package com.ylbtech.api.platform.controller.auth; import com.ylbtech.api.platform.core.jwt.JwtConfigurationProperties; import com.ylbtech.api.platform.core.jwt.JwtUtil; import com.ylbtech.api.platform.core.response.Result; import com.ylbtech.api.platform.core.response.ResultGenerator; import com.ylbtech.edu.organization.domain.Organization; import com.ylbtech.edu.organization.service.IOrganizationService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j @Api(value = "帳戶接口") @Validated @RestController @RequestMapping("/user") public class AuthController { @Resource private JwtUtil jwtUtil; @Autowired private IOrganizationService organizationService; /** * showdoc * @catalog 用戶 * @title 帳戶註銷 * @description 帳戶註銷接口 * @method Delete * @url https://ip:port/user/token/logout * @param adminID 必選 string 帳號 * @return {"code":200} * @remark */ @ApiOperation(value = "帳戶註銷") @PostMapping("/token/logout") public Result logout(@ApiParam(required = true) @RequestBody OrganizationAdmin organizationAdmin) { if (organizationAdmin.getAdminID() == null || organizationAdmin.getAdminID().equalsIgnoreCase("")) { return ResultGenerator.genFailedResult("adminID is null"); } this.jwtUtil.invalidRedisToken(organizationAdmin.getAdminID()); return ResultGenerator.genOkResult(); } }
3.返回頂部 |
4.返回頂部 |
/* * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package javax.annotation; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * The Resource annotation marks a resource that is needed * by the application. This annotation may be applied to an * application component class, or to fields or methods of the * component class. When the annotation is applied to a * field or method, the container will inject an instance * of the requested resource into the application component * when the component is initialized. If the annotation is * applied to the component class, the annotation declares a * resource that the application will look up at runtime. <p> * * Even though this annotation is not marked Inherited, deployment * tools are required to examine all superclasses of any component * class to discover all uses of this annotation in all superclasses. * All such annotation instances specify resources that are needed * by the application component. Note that this annotation may * appear on private fields and methods of superclasses; the container * is required to perform injection in these cases as well. * * @since Common Annotations 1.0 */ @Target({TYPE, FIELD, METHOD}) @Retention(RUNTIME) public @interface Resource { /** * The JNDI name of the resource. For field annotations, * the default is the field name. For method annotations, * the default is the JavaBeans property name corresponding * to the method. For class annotations, there is no default * and this must be specified. */ String name() default ""; /** * The name of the resource that the reference points to. It can * link to any compatible resource using the global JNDI names. * * @since Common Annotations 1.1 */ String lookup() default ""; /** * The Java type of the resource. For field annotations, * the default is the type of the field. For method annotations, * the default is the type of the JavaBeans property. * For class annotations, there is no default and this must be * specified. */ Class<?> type() default java.lang.Object.class; /** * The two possible authentication types for a resource. */ enum AuthenticationType { CONTAINER, APPLICATION } /** * The authentication type to use for this resource. * This may be specified for resources representing a * connection factory of any supported type, and must * not be specified for resources of other types. */ AuthenticationType authenticationType() default AuthenticationType.CONTAINER; /** * Indicates whether this resource can be shared between * this component and other components. * This may be specified for resources representing a * connection factory of any supported type, and must * not be specified for resources of other types. */ boolean shareable() default true; /** * A product specific name that this resource should be mapped to. * The name of this resource, as defined by the <code>name</code> * element or defaulted, is a name that is local to the application * component using the resource. (It's a name in the JNDI * <code>java:comp/env</code> namespace.) Many application servers * provide a way to map these local names to names of resources * known to the application server. This mapped name is often a * <i>global</i> JNDI name, but may be a name of any form. <p> * * Application servers are not required to support any particular * form or type of mapped name, nor the ability to use mapped names. * The mapped name is product-dependent and often installation-dependent. * No use of a mapped name is portable. */ String mappedName() default ""; /** * Description of this resource. The description is expected * to be in the default language of the system on which the * application is deployed. The description can be presented * to the Deployer to help in choosing the correct resource. */ String description() default ""; }
5.返回頂部 |
package com.ylbtech.api.platform.core.jwt; import com.ylbtech.api.platform.core.rsa.RsaUtils; import com.ylbtech.api.platform.util.RedisUtils; import io.jsonwebtoken.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.time.Duration; import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; /** * Json web token 工具 驗證、生成 token */ @Slf4j @Component public class JwtUtil { @Resource private RedisUtils redisUtils; @Resource private RsaUtils rsaUtils; @Resource private JwtConfigurationProperties jwtProperties; private Claims getClaims(final String token) { final Jws<Claims> jws = this.parseToken(token); return jws == null ? null : jws.getBody(); } /** * 根據 token 獲得帳戶名 */ public String getName(final String token) { final Claims claims = this.getClaims(token); return claims == null ? null : claims.getSubject(); } /** * 簽發 token * * @param name 帳戶名 * @param grantedAuthorities 帳戶權限信息[ADMIN, TEST, ...] */ public String sign( final String name, final Collection<? extends GrantedAuthority> grantedAuthorities) { // 函數式建立 token,避免重複書寫 final Supplier<String> createToken = () -> this.createToken(name, grantedAuthorities); // 看看緩存有沒有帳戶token final String token = (String) this.redisUtils.getValue(name); // 沒有登陸過 if (StringUtils.isBlank(token)) { return createToken.get(); } final boolean isValidate = (boolean) this.redisUtils.getValue(token); // 有 token,仍有效,將 token 置爲無效,並從新簽發(防止 token 被利用) if (isValidate) { this.invalidRedisToken(name); } // 從新簽發 return createToken.get(); } /** * 清除帳戶在 Redis 中緩存的 token * * @param name 帳戶名 */ public void invalidRedisToken(final String name) { // 將 token 設置爲無效 final String token = (String) this.redisUtils.getValue(name); Optional.ofNullable(token).ifPresent(_token -> this.redisUtils.setValue(_token, false)); } /** * 從請求頭或請求參數中獲取 token */ public String getTokenFromRequest(final HttpServletRequest httpRequest) { final String header = this.jwtProperties.getHeader(); final String token = httpRequest.getHeader(header); return StringUtils.isNotBlank(token) ? token : httpRequest.getParameter(header); } /** * 返回帳戶認證 */ public UsernamePasswordAuthenticationToken getAuthentication( final String name, final String token) { // 解析 token 的 payload final Claims claims = this.getClaims(token); // 由於 JwtAuthenticationFilter 攔截器已經檢查過 token 有效,因此能夠忽略 get 空指針提示 assert claims != null; final String claimKeyAuth = this.jwtProperties.getClaimKeyAuth(); // 帳戶角色列表 final List<String> authList = Arrays.asList(claims.get(claimKeyAuth).toString().split(",")); // 將元素轉換爲 GrantedAuthority 接口集合 final Collection<? extends GrantedAuthority> authorities = authList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); final User user = new User(name, "", authorities); return new UsernamePasswordAuthenticationToken(user, null, authorities); } /** * 驗證 token 是否正確 */ public boolean validateToken(final String token) { boolean isValidate = true; final Object redisTokenValidate = this.redisUtils.getValue(token); // 可能 redis 部署出現了問題 // 或者清空了緩存致使 token 鍵不存在 if (redisTokenValidate != null) { isValidate = (boolean) redisTokenValidate; } // 能正確解析 token,而且 redis 中緩存的 token 也是有效的 return this.parseToken(token) != null && isValidate; } /** * 生成 token */ private String createToken( final String name, final Collection<? extends GrantedAuthority> grantedAuthorities) { // 獲取帳戶的角色字符串,如 USER,ADMIN final String authorities = grantedAuthorities .stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); log.debug("==> user<{}> authorities: {}", name, authorities); // 過時時間 final Duration expireTime = this.jwtProperties.getExpireTime(); // 當前時間 + 有效時長 final Date expireDate = new Date(System.currentTimeMillis() + expireTime.toMillis()); // 建立 token,好比 "Bearer abc1234" final String token = this.jwtProperties.getTokenType() + " " + Jwts.builder() // 設置帳戶名 .setSubject(name) // 添加權限屬性 .claim(this.jwtProperties.getClaimKeyAuth(), authorities) // 設置失效時間 .setExpiration(expireDate) // 私鑰加密生成簽名 .signWith(SignatureAlgorithm.RS256, this.rsaUtils.loadPrivateKey()) // 使用LZ77算法與哈夫曼編碼結合的壓縮算法進行壓縮 .compressWith(CompressionCodecs.DEFLATE) .compact(); // 保存帳戶 token // 由於帳戶註銷後 JWT 自己只要沒過時就仍然有效,因此只能經過 redis 緩存來校驗有無效 // 校驗時只要 redis 中的 token 無效便可(JWT 自己能夠校驗有無過時,而 redis 過時即被刪除了) // true 有效 this.redisUtils.setValue(token, true, expireTime); // redis 過時時間和 JWT 的一致 this.redisUtils.setValue(name, token, expireTime); log.debug("==> Redis set uid<{}> token: {}", name, token); return token; } /** * 解析 token */ private Jws<Claims> parseToken(final String token) { try { return Jwts.parser() // 公鑰解密 .setSigningKey(this.rsaUtils.loadPublicKey()) .parseClaimsJws(token.replace(this.jwtProperties.getTokenType(), "")); } catch (final SignatureException e) { // 簽名異常 log.debug("Invalid JWT signature"); } catch (final MalformedJwtException e) { // 格式錯誤 log.debug("Invalid JWT token"); } catch (final ExpiredJwtException e) { // 過時 log.debug("Expired JWT token"); } catch (final UnsupportedJwtException e) { // 不支持該JWT log.debug("Unsupported JWT token"); } catch (final IllegalArgumentException e) { // 參數錯誤異常 log.debug("JWT token compact of handler are invalid"); } return null; } }
6.返回頂部 |
![]() |
做者:ylbtech 出處:http://ylbtech.cnblogs.com/ 本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。 |