SpringBoot AOP 基於前後端分離的登入攔截設計
阿新 • • 發佈:2019-02-10
一、前言
前後端分離開發是目前軟體開發的主流,大大提高了開發效率
但也帶來了很多不方便之處。
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 去登入