什麼是Api接口冪等?
簡單來說Api接口冪等在有限的時間內限制接口訪問請求,限制ip訪問次數,不限制平台訪問,都可以拿到數據。一個接口不可以重複表單提交,生產一次消費一次。
用戶場景:同一時間重複提交多次請求。
什麼是數據篡改?
api接口數據篡改,腳本文件,篡改接口參數進行服務器數據竊取,嚴重的數據篡改會導致數據庫宕機,程序軟件崩潰。
想到這裡都知道後台api接口冪等多重要了吧。今天給大家講非對稱加密實現後台接口api冪等。
實現思路:jtw+ 驗證標識+簽名密鑰+當前時間戳+存放過期時間+AES 實現加密算法token。
實現步驟:1,用戶登錄成功後,生產加密token存放redis.
2,下次登錄檢驗token 是否過期,過期請重新登錄。
3,用戶登錄存在有效期,不需要登錄。(這裡就是單點登錄方式)
code核心實現類:
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
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 org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class JWTTokenUtils {
	public static final String AUTHORIZATION_HEADER = "x-token";
	public static final String AUTHORIZATION_TOKEN = "x-token";
	private final Logger logger = LoggerFactory.getLogger(JWTTokenUtils.class);
	private static final String AUTHORITIES_KEY = "auth";
	private String secretKey; // 簽名密鑰
	private long tokenValidityInMilliseconds; // 失效日期
	private long tokenValidityInMillisecondsForRememberMe; // (記住我)失效日期
	@PostConstruct
	public void init() {
		this.secretKey = "isoftstone.huwei";
		int secondIn1day = 1000 * 60 * 60 * 24;
		this.tokenValidityInMilliseconds = secondIn1day * 2L;
		this.tokenValidityInMillisecondsForRememberMe = secondIn1day * 7L;
	}
	// 創建Token
	public String createToken(Authentication authentication, Boolean rememberMe) {
		String authorities = authentication.getAuthorities().stream() // 獲取用戶的權限字符串,如 USER,ADMIN
				.map(GrantedAuthority::getAuthority).collect(Collectors.joining(","));
		long now = (new Date()).getTime(); // 獲取當前時間戳
		Date validity; // 存放過期時間
		if (rememberMe) {
			validity = new Date(now + this.tokenValidityInMilliseconds);
		} else {
			validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
		}
		return SysConst.SYS_COMPANY_HEAD+"."+ Jwts.builder() // 創建Token令牌
				.setSubject(authentication.getName()) // 設置面向用戶
				.claim(AUTHORITIES_KEY, authorities) // 添加權限屬性
				.setExpiration(validity) // 設置失效時間
				.signWith(SignatureAlgorithm.HS512, secretKey) // 生成簽名
				.compact();
	}
	// 獲取用戶權限
	public Authentication getAuthentication(String token) {
		logger.info("JWTTokenUtils Start Get User Auth");
		// 解析Token的payload
		Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
		Collection<? extends GrantedAuthority> authorities = Arrays
				.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) // 獲取用戶權限字符串
				.map(SimpleGrantedAuthority::new).collect(Collectors.toList()); // 將元素轉換為GrantedAuthority接口集合
		User principal = new User(claims.getSubject(), "", authorities);
		return new UsernamePasswordAuthenticationToken(principal, null, authorities);
	}
	/**
	 * 解析token獲取用戶編碼
	 * @param token
	 * @return
	 */
	public String getAuthSubject(String token) {
		Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
		return claims.getSubject();
	}
	
	public String resolveToken(HttpServletRequest request){
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);         //從HTTP頭部獲取TOKEN
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(SysConst.SYS_COMPANY_HEAD)){
            return bearerToken.substring(bearerToken.indexOf(".")+1, bearerToken.length());                              //返回Token字符串,去除Bearer
        }
        String jwt = request.getParameter(AUTHORIZATION_TOKEN);               //從請求參數中獲取TOKEN
        if (StringUtils.hasText(jwt) && jwt.startsWith(SysConst.SYS_COMPANY_HEAD)) {
            return jwt.substring(bearerToken.indexOf(".")+1, jwt.length());
        }
        return null;
    }
	// 驗證Token是否正確
	public boolean validateToken(String token) {
		try {
			Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); // 通過密鑰驗證Token
			return true;
		}  catch (MalformedJwtException e) { // JWT格式錯誤
			logger.info("Invalid JWT token.");
			logger.trace("Invalid JWT token trace: {}", e);
		} catch (ExpiredJwtException e) { // JWT過期
			logger.info("Expired JWT token.");
			logger.trace("Expired JWT token trace: {}", e);
		} catch (UnsupportedJwtException e) { // 不支持該JWT
			logger.info("Unsupported JWT token.");
			logger.trace("Unsupported JWT token trace: {}", e);
		} catch (IllegalArgumentException e) { // 參數錯誤異常
			logger.info("JWT token compact of handler are invalid.");
			logger.trace("JWT token compact of handler are invalid trace: {}", e);
		}catch (SignatureException e) { // 簽名異常
			logger.info("Invalid JWT signature.");
			logger.trace("Invalid JWT signature trace: {}", e);
		}
		return false;
	}
}
redis寫入緩存:
   RedisModel model = new RedisModel();
   model.setModelName(token);
   model.setModelKey("userInfo");
   Map<String,Object> params = new HashMap<String,Object>();
   params.put("userName", customerDto.getUsername());
   CustomerVo customerVo = new CustomerVo();
   customerVo.setEmail(customerDto.getEmail());
   customerVo.setPhone(customerDto.getPhone());
   CustomerVo userInfo = customerService.queryUserByUserName(customerVo);
   Map<String,Object> userToken = new HashMap<String,Object>();
   userToken.put("userToken", token);
   userToken.put("userInfo", userInfo);
   model.setModelData(userToken);
   model.setTimeoutType("M");
   model.setTimeout(redisTokenTimeOut);
   resultMap.setCode(CommonResultStatus.SUCCESS.getCode());
   resultMap.setMessage(CommonResultStatus.SUCCESS.getMessage());
   //寫入為hash實體
   redisTemplate.opsForHash().put(model.getModelName(), model.getModelKey(), model.getModelData());
   redisTemplate.expire(model.getModelName(), model.getTimeout(), TimeUnit.MINUTES);@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	public static final String AUTHORIZATION_HEADER = "x-token";//Authorization
	//允許訪問的路徑
	private static final String[] AUTH_WITHOUTLIST = {
			// -- swagger ui
			"/v2/api-docs", "/swagger-resources", "/swagger-resources/**", "/configuration/ui",
			"/configuration/security", "/swagger-ui.html", "/webjars/**",
			// other public endpoints of your API may be appended to this array
			"/druid/**","/health","/info","/info/**",//druid sql 監聽
			"/hystrix","/hystrix/**","/error","/loggers","/loggers/**",
			"/service-registry/instance-status","/hystrix.stream","/turbine/**","/turbine.stream",
			"/autoconfig","/archaius","/beans","/features","/configprops","/mappings","/auditevents",
			"/env","/env/**","/metrics","/metrics/**","/trace","/trace/**", "/dump","/dump/**", "/jolokia/**",
			"/info/**","/activiti/**", "/logfile/**", "/refresh","/flyway/**", "/liquibase/**","/heapdump","/heapdump/**",
			"/v1/authcenter/login","/v1/authcenter/fiberhomeLogin","/v1/authcenter/registered",//登錄URL
			"/v1/authcenter/queryAuthInfo",//鑒權URL
			"/u/sms/sendPhone","/citry/getChineseProvinces","/code/getCaptchaImage","/u/sms/forgetEmailPwd",
			"/u/sms/sendEmail","/citry/getOtherCoutryList","/upload/pngDir/*","/job/getJobList","/u/sms/sendLoginEmail",
			"/v1/authcenter/queryUserInfoByMap","/v1/authcenter/forgetPwd",
			"/diagram-viwmer/**","/editor-marketing/**",
			"/modeler.html",
			"/actuator/health"
	};
	@Autowired
	private SecurityUserDetailsService securityUserDetailsService;
	@Autowired
	private AuthLogoutSuccessHandler authLogoutSuccessHandler;
	@Autowired
	private JWTTokenUtils tokenProvider;
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//自定義獲取用戶信息設置密碼加密  和默認用DaoAuthenticationProvider加密任選
		auth.userDetailsService(securityUserDetailsService).passwordEncoder(passwordEncoder());
	}
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// 配置請求訪問策略
		// 關閉CSRF、CORS
		http.cors().disable().csrf().disable()
				// 由於使用Token,所以不需要Session
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
				// 驗證Http請求
				.authorizeRequests()
				// 允許所有用戶訪問首頁 與 登錄
				.antMatchers(AUTH_WITHOUTLIST).permitAll()
				// 其它任何請求都要經過認證通過
				.anyRequest().authenticated().and()
				// 用戶頁面需要用戶權限
				// 設置登出
				.logout().logoutSuccessHandler(authLogoutSuccessHandler).permitAll();
		// 添加JWT filter 在
		http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
	}
//判斷是否有權限分三步 
後台security 已經對地址做了攔截了,請求頭必須設置請求參數參數
1:判斷token是否存在(security 已實現)
2:token是否有效(基於redis) 
3:訪問API是否有權限
緩存結構:
[
  "java.util.HashMap",
  {
    "userToken": "ISOFTSTONE.eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI0MmRkMGJlZmQwNDg0MmMyODhiN2QxZjVkYTcwMWNjNiIsImF1dGgiOiJ1c2VyIiwiZXhwIjoxNjM1MjMyMzk4fQ.9VqMD0vyu-pe42moNd1QeNWP4KrBvvNYJKDQdQPDi_YKKXPG3l90dNn5wgK2rZXs471Pmeby-BdHPHd2-iNKfA",
    "userInfo": [
      "com.common.vo.CustomerVo",
      {
        "id": "42dd0befd04842c288b7d1f5da701cc6",
        "createTime": [
          "java.util.Date",
          "2021-06-10"
        ],
        "updateTime": [
          "java.util.Date",
          "2021-06-10"
        ],
        "deleted": 0,
        "phone": "13797004616",
        "password": "123456",
        "email": "sunlin@fiberhome.com",
        "status": "3",
        "isEnable": "1",
        "userType": "1",
        "roleId": null,
        "country": null,
        "provinces": null,
        "company": null,
        "jobs": null,
        "realName": "孫",
        "fiberhomeEmail": null,
        "department": null,
        "registerWay": null,
        "onlineTime": null,
        "expireDate": null,
        "registerIp": null,
        "language": null,
        "equipmentType": null,
        "accountType": null,
        "platformType": null,
        "companyType": null,
        "userdesc": null,
        "userid": "0210990342",
        "typeConfig": null,
        "isActive": "1",
        "postname": "副總經理",
        "did1": "701387",
        "dname1": "財務管理部",
        "did2": null,
        "dname2": null,
        "did3": null,
        "dname3": null,
        "did4": null,
        "dname4": null,
        "postid": "803711"
      }
    ]
  }
]接口實現返回:
x-token:ISOFTSTONE.eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI0MmRkMGJlZmQwNDg0MmMyODhiN2QxZjVkYTcwMWNjNiIsImF1dGgiOiJ1c2VyIiwiZXhwIjoxNjM1MjMyNTMzfQ.4QzaY_ln-EVukG6Hnbx-mRl2nUB0Eu6jJxIN0KHigzLfqZH7aXt1aId0RhRvS-o5Vf7cVp5jd9cqpkQwfp724A

請求其他接口訪問,加入到請求頭Headers裏面即可。我們的api接口冪等就實現完成了。
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/203765.html
 
 微信掃一掃
微信掃一掃  支付寶掃一掃
支付寶掃一掃 