首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

App 登錄與權限驗證實戰:Spring AOP + 自定義註解實現 Token 機制

     編輯:Java 綜合教學

App 登錄與權限驗證實戰:Spring AOP + 自定義註解實現 Token 機制

專案中後台功能已開發大半,卻遺漏了最基礎的用戶登錄與權限驗證。面對無規律的既有 URL,傳統過濾器/攔截器難以優雅應用。本文展示如何利用 Spring AOP 配合自定義註解,以最小改動實現靈活的 Token 權限控制。

核心需求

  1. 既有代碼改動最小化,新功能接入權限驗證無感
  2. 用戶名/密碼換取 Token,後續請求攜帶 Token 驗證身份
  3. 支援單點登錄:Token 動態生成,防止多端同時在線

技術方案:AOP + 自定義註解

1. 開啟 AOP 支援

<aop:aspectj-autoproxy />

需引入 spring-aop、aspectjweaver 依賴,並在 Spring 配置文件頭部聲明 AOP 命名空間。

2. 定義權限註解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAccess {
    // 可擴展:角色、權限碼等屬性
}

3. Token 生成與管理服務

@Service
public class LoginService {
    // "username -> token"
    public static final Map<String, String> tokenMap = new ConcurrentHashMap<>();
    // "token -> User"
    public static final Map<String, User> loginUserMap = new ConcurrentHashMap<>();
    
    public String login(String name, String password) {
        // 1. 驗證用戶名密碼(略,對接 UserDao)
        // 2. 生成 Token:username + password + timestamp → MD5
        String token = MD5Util.MD5(name + password + System.currentTimeMillis());
        
        // 3. 處理單點登錄:移除舊 Token
        String oldToken = tokenMap.get(name);
        if (oldToken != null) {
            loginUserMap.remove(oldToken);
        }
        
        // 4. 存儲新映射
        User user = new User(name, password); // 實際從 DB 獲取
        tokenMap.put(name, token);
        loginUserMap.put(token, user);
        
        return token;
    }
}

Token 設計要點:

  • 輸入因子包含時間戳,確保每次登錄 Token 不同
  • 雙向 Map 實現 O(1) 正向/反向查找
  • 生產環境建議改用 Redis,支援集群、過期自動清理

4. 登錄 Controller(無需 Token 驗證)

@Controller
@RequestMapping("/login")
public class LoginController {
    @Autowired
    private LoginService loginService;
    
    @RequestMapping(value="/loginin", method=RequestMethod.GET)
    @ResponseBody
    public String login(HttpServletRequest request) {
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        return loginService.login(name, password);
    }
}

5. AOP 切面:統一權限驗證

@Component
@Aspect
public class PermissionAspect {
    @Before("@annotation(com.example.annotation.UserAccess)")
    public void checkPermission(JoinPoint joinPoint) throws Exception {
        Object[] args = joinPoint.getArgs();
        HttpServletRequest request = (HttpServletRequest) args[0];
        
        // 建議從 Header 獲取 Token,不侵業務參數
        String token = request.getHeader("token");
        if (token == null) {
            token = request.getParameter("token"); // 兼容 Query 參數
        }
        
        User user = LoginService.loginUserMap.get(token);
        if (user == null) {
            throw new Exception("無效或過期的 Token,請重新登錄");
        }
        // 可選:將 User 放入 ThreadLocal 或 Request 屬性,供業務方法直接使用
        request.setAttribute("currentUser", user);
    }
}

6. 業務方法接入:僅需加註解

@Controller
@RequestMapping("/order")
public class OrderController {
    @UserAccess // ← 這裡
    @RequestMapping("/list")
    @ResponseBody
    public List<Order> listOrders(HttpServletRequest request) {
        User user = (User) request.getAttribute("currentUser");
        return orderService.getOrdersByUser(user.getId());
    }
}

方案優勢總結

維度傳統過濾器/攔截器AOP + 註解方案
URL 規則要求需統一前綴/模式無要求,方法級精確控制
既有代碼改動大(需調整 URL 映射)極小(僅加註解)
靈活性粗粒度細粒度,可擴展註解屬性
Token 獲取方式通常固定參數名Header/Parameter 靈活配置

生產環境建議優化

  • Token 存儲遷移至 Redis,設定 TTL 實現自動過期
  • 引入 JWT (JSON Web Token) 實現無狀態驗證,攜帶用戶 ID、角色、過期時間等 Claims
  • 切面中統一異常處理,返回標準化錯誤碼(如 401 Unauthorized)
  • 支援註解屬性:@UserAccess(roles={"ADMIN","USER"}) 實現 RBAC
  • Token 傳輸強制 HTTPS,防止中間人竊聽

完整源碼參考:https://github.com/zw201913/applogin.git

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved