首頁 > Java > java教程 > 怎麼在SpringBoot中使用Spring AOP實作介面鑑權

怎麼在SpringBoot中使用Spring AOP實作介面鑑權

WBOY
發布: 2023-05-19 18:13:12
轉載
1306 人瀏覽過

面向切面編程

面向切面編程,可以將與業務無關但是需要被各個業務模組共同調用的邏輯抽取出來,以切面的方式切入到代碼中,從而降低系統中代碼的耦合度,減少重複的程式碼。

Spring AOP是透過預編譯方式和運行期間動態代理實現程式面向切面程式設計

AOP的底層原理實作

AOP底層使用動態代理完成需求,為需要增加增強功能的類別來產生代理類,有兩種產生代理類別的方式,對於被代理類別(即需要增強的類別),如果:

  • #實作了接口,使用JDK動態代理,產生的代理類別會使用其介面沒有實作接口,

  • 使用CGlib動態代理,產生的代理類別會整合被代理類別

AOP的相關術語

  • #連接點:在被代理(被增強)的類別中的方法

  • 切入點:實際上需要被增強的方法

  • ##通知:要增強的邏輯代碼

    • 前通知:在主體功能執行前執行

    • 後置通知:在主題功能執行之後執行

    • 環繞通知:在主體功能執行前後執行

    • #異常通知:在主題功能執行出現異常時執行

    • #最終通知:主體功能無論執行是否成功都會執行

  • 切面:切入點和切面的結合,即被增強的方法和增強的功能組成切面

相關註解以及切入點表達式

註解:

  • #@Aspect: 宣告某個類別是切面,寫通知、切入點

  • @Before: 對應前置通知

  • @AfterReturning: 對應後置通知

  • @Around: 對應環繞通知

  • ##@AfterThrowing:

    對應例外通知

  • @After:

    對應最終通知

  • @Pointcut:

    聲明切入點,標註在一個方法上可以讓表達式更簡潔

使用切入點表達式宣告切入點

    execution([權限修飾符][傳回類型][類別完全路徑].[方法名稱][參數清單類型])
  • execution(* com.xxx.ABC. add()),對ABC類別的方法進行增強

實作介面鑑權

1. 設定yml檔案

設定介面鑑權帳密

account:
  infos:
    - account: xinchao
      secret: admin
登入後複製

2. 讀取帳密配置
@Data
public class SecretInfo {
    private String account;
    private String secret;
}
登入後複製

3.寫介面鑑權方法

@Configuration
@ConfigurationProperties("account")
public class SecretConfig {
    private List<SecretInfo> infos;

    private Map<String, SecretInfo> map;

    private Map<String, TokenInfo> tokenMap = new HashMap<>();

    public void setInfos(List<SecretInfo> infos) {
        this.infos = infos;
        map = infos.stream().collect(Collectors.toMap(SecretInfo::getAccount, Function.identity()));
    }

    public synchronized String getToken(String account, String secret) {
        SecretInfo info = map.get(account);
        if (info == null) {
            throw new BusinessException("无效账号");
        }
        if (!StringUtils.equals(info.getSecret(), secret)) {
            throw new BusinessException("无效密码");
        }
        TokenInfo tokenInfo = tokenMap.get(account);
        if (tokenInfo != null && tokenInfo.getToken() != null) {
            return tokenInfo.getToken();
        }
        tokenInfo = new TokenInfo();
        String uuid = UUID.randomUUID().toString();
        tokenInfo.setToken(uuid);
        tokenInfo.setCreateDate(LocalDateTime.now());
        tokenInfo.setExpireDate(LocalDateTime.now().plusHours(2));
        tokenMap.put(account,tokenInfo);
        return tokenInfo.getToken();
    }

    public boolean checkCaptcha(String captcha) {
        return tokenMap.values().stream().anyMatch(e->StringUtils.equals(e.getToken(),captcha));
    }
}
登入後複製
@Data
public class TokenInfo {
    private LocalDateTime createDate;
    private LocalDateTime expireDate;
    private String token;

    public String getToken() {
        if (LocalDateTime.now().isBefore(expireDate)) {
            return token;
        }
        return null;
    }

    public boolean verification(String token) {
        return Objects.equals(this.token, token);
    }
}
登入後複製

4. 寫AOP

首先,編寫一個註解來標識不需要鑑權

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CaptchaIgnoreAop {
}
登入後複製
@Slf4j
@Aspect
@Component
@Order(2)
public class CaptchaAop {

    @Value("${spring.profiles.active:dev}")
    private String env;

    @Autowired
    private SecretConfig config;

    @Pointcut("execution(public * com.herenit.phsswitch.controller.impl..*.*(..))" +
            "&&@annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "&&!@annotation(com.herenit.phsswitch.aop.CaptchaIgnoreAop)")
    public void tokenAop() {
    }

    @Around("tokenAop()")
    public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if (args.length == 0 || !(args[0] instanceof RequestWrapper)
                || "test,dev".contains(env)) {
            log.info("当前环境无需校验token");
            return joinPoint.proceed();
        }
        String captcha = ((RequestWrapper) joinPoint.getArgs()[0]).getCaptcha();
        if (!config.checkCaptcha(captcha)) {
            throw new BusinessException("captcha无效");
        }
        return joinPoint.proceed();
    }

}
登入後複製

5.編寫介面測試
@PostMapping("/login")
@CaptchaIgnoreAop
public ResponseWrapper login(@RequestBody JSONObject userInfo) {
    String token = config.getToken(userInfo.getString("loginName")
            , userInfo.getString("password"));
    JSONObject result = new JSONObject();
    result.put("platformAccessToken", token);
    return ResponseWrapper.success(result);
}
登入後複製

使用此介面能夠在記憶體中建立一個令牌,並將其傳回給前端。之後我們在調其他介面時傳入這個token進行鑑權即可。傳入的位置是captcha字段

public class RequestWrapper<T> implements Serializable {

    private static final long serialVersionUID = 8988706670118918321L;
    public RequestWrapper() {
        super();
    }

    private T args;

    private String captcha;

    private String funcode;

    public T getArgs() {
        return args;
    }

    public void setArgs(T args) {
        this.args = args;
    }

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }

    public String getFuncode() {
        return funcode;
    }

    public void setFuncode(String funcode) {
        this.funcode = funcode;
    }
}
登入後複製

以上是怎麼在SpringBoot中使用Spring AOP實作介面鑑權的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板