1. 程式人生 > >SpringBoot AOP 基於前後端分離的登入攔截設計

SpringBoot AOP 基於前後端分離的登入攔截設計

一、前言

前後端分離開發是目前軟體開發的主流,大大提高了開發效率
但也帶來了很多不方便之處。

1、優點:
① 傳統全棧開發的 MVC 模式將不適合,後臺採取 MVP 面向介面程式設計,耦合度大大降低

2、缺點:
① 跨域問題不勝其擾

3、原則:
① 一個合格的後臺應全力負責業務邏輯
② 前端不要參與過多的業務邏輯,專注於網頁的視覺建設
否則專案耦合度高、網站的安全性低

二、思路

學過計算機網路的,都知道,每個人的電腦的 ip 地址是全球唯一的,
我們可以利用 ip 地址在資料庫中的記錄中的 查詢、增加、刪除,來進行 攔截、登入、登出
使用 AOP 程式設計,對指定的方法進行登入攔截

三、程式碼

下面以個人最近開發的一個校園外賣網站的後臺程式碼為例(待續),抽取分享相關的程式碼

1、程式碼結構

這裡寫圖片描述
這裡寫圖片描述

2、登入實體 Login.java

package com.cun.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import
io.swagger.annotations.ApiModelProperty; @Entity @Table(name = "t_login") public class Login { @Id @GeneratedValue @ApiModelProperty(value = "主鍵id") private Integer id; @Column(length = 100) private String addressIp; //登入後的主機 ip private Date date; //登陸時間 private Integer userId; //關聯登入的使用者id
public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAddressIp() { return addressIp; } public void setAddressIp(String addressIp) { this.addressIp = addressIp; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } }

3、Dao層 LoginDao.java

package com.cun.dao;


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import com.cun.entity.Login;

public interface LoginDao extends JpaRepository<Login, Integer>{

    // 注意:delete 操作,dao 介面層加 @Modifying ,在呼叫層使用 @Transactional
    @Modifying
    @Query(value = "delete from t_login where address_ip=?1", nativeQuery = true)
    void deleteLogin(String addressIp);

    @Query(value = "select * from t_login where address_ip=?1 order by date desc limit 1", nativeQuery = true)
    Login getLoginByIp(String addressIp);

}

4、安全控制 MySecurity.java

構思最為長久的地方

package com.cun.security;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.cun.dao.LoginDao;
import com.cun.entity.Login;
import com.cun.util.Json;

@Component
@Aspect
public class MySecurity {

    @Autowired
    private LoginDao loginDao;

    private final Logger logger = LoggerFactory.getLogger(MySecurity.class);

    @Pointcut("execution(public * com.cun.controller.admin.*.*(..))")
    public void log() {
    }

    @Before("log()")
    public void deoBefore(JoinPoint joinPoint) {
        logger.info("方法執行前...");

        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        logger.info("url:" + request.getRequestURI());
        logger.info("ip:" + request.getRemoteHost());
        logger.info("method:" + request.getMethod());
        logger.info("class_method:" + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        logger.info("args:" + joinPoint.getArgs());
    }

    @After("log()")
    public void doAfter(JoinPoint joinPoint) {
        logger.info("方法執行後...");
    }

    @AfterReturning(returning = "result", pointcut = "log()")
    public void doAfterReturning(Object result) {
        logger.info("執行返回值:" + result);
    }

    @Around("log()")
    public Object trackInfo(ProceedingJoinPoint pjp) throws Throwable {

        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        String remoteHost = request.getRemoteHost();
        Login loginByIp = loginDao.getLoginByIp(remoteHost);

        if (loginByIp == null) {
            logger.info("-------------沒有登入-------------");
            return Json.fail();
        } else {
            logger.info("-------------登入通過-------------");
        }
        return pjp.proceed();
    }

}

5、通用返回值工具類簡單設計

package com.cun.util;

import java.util.HashMap;
import java.util.Map;

public class Json {

    public static Map<String, Object> success(Object data) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", 200);
        map.put("msg", "ok");
        map.put("data", data);
        return map;
    }

    public static Map<String, Object> fail() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", 400);
        map.put("msg", "error");
        return map;
    }

}

四、最後

其實這種做法是不安全的,只能攔截沒有程式設計知識的小白群眾,使用者可以使用代理 ip 等技術,獲取他人的 ip 去登入