Apache Shiro安全框架詳解:認證、授權與加密功能

2024-12-27 14:11 更新

大家好,我是 V 哥。Apache Shiro 是一個強大且靈活的 Java 安全框架,專注于提供認證、授權、會話管理和加密功能。它常用于保護 Java 應用的訪問控制,特別是在 Web 應用中。相比于 Spring Security,Shiro 的設計更簡潔,適合輕量級應用,并且在許多方面具有更好的易用性和擴展性,今天 V 哥就來聊聊 Shiro 安全框架。

Shiro 的核心概念

按照慣例,和 V 哥一起來了解一下 Shiro 的核心概念:

  1. Subject
    Subject 是 Shiro 框架中一個核心的接口,表示應用中的“用戶”或“實體”,用于交互和存儲認證狀態(tài)。通常通過 SecurityUtils.getSubject() 獲取當前的 Subject。它代表了用戶的身份信息和權限數(shù)據(jù)。

  1. SecurityManager
    SecurityManager 是 Shiro 的核心控制器,負責管理所有的安全操作和認證。通過配置 SecurityManager,可以控制用戶的認證、授權、會話等管理。

  1. Realm
    Realm 是 Shiro 從數(shù)據(jù)源獲取用戶、角色和權限信息的途徑。通過實現(xiàn)自定義的 Realm,可以將 Shiro 與數(shù)據(jù)庫、LDAP、文件等數(shù)據(jù)源整合。Shiro 會把用戶的認證和授權數(shù)據(jù)從 Realm 中獲取。

  1. Session
    Shiro 自帶會話管理,不依賴于 Servlet 容器提供的會話。即使在非 Web 環(huán)境下,也可以使用 Shiro 的會話管理。Shiro 的會話管理提供了更細致的控制,比如會話超時、存儲和共享等功能。

  1. Authentication(認證)
    認證是指驗證用戶身份的過程。Shiro 提供了簡單的 API 來實現(xiàn)認證過程,比如 subject.login(token)。在實際應用中,通常通過用戶名和密碼的組合進行認證,但 Shiro 也支持其他方式(如 OAuth2、JWT 等)。

  1. Authorization(授權)
    授權是指驗證用戶是否具備某些權限或角色的過程。Shiro 支持基于角色和基于權限的授權,允許更精細的權限控制。通過 subject.hasRolesubject.isPermitted 方法,開發(fā)者可以檢查用戶的角色和權限。

  1. Cryptography(加密)
    Shiro 內(nèi)置了加密功能,提供對密碼和敏感信息的加密和解密支持。它支持多種加密算法,并且在密碼存儲時支持散列和鹽值。

Shiro 的主要功能和優(yōu)勢

V 哥總結幾點Shiro 的主要功能和優(yōu)勢,這個在面試時吹牛逼用得到。

  1. 易于集成
    Shiro 的 API 設計簡單,易于集成到各種 Java 應用中。開發(fā)者可以基于 Shiro 提供的默認實現(xiàn)快速搭建一個基本的安全架構,也可以根據(jù)需要自定義各種功能。

  1. 獨立的會話管理
    與基于 Web 容器的會話管理不同,Shiro 提供了跨環(huán)境的會話管理,可以應用于 Web 和非 Web 的環(huán)境,增加了應用的靈活性。

  1. 權限控制簡單而靈活
    Shiro 的權限管理可以通過配置文件、注解或代碼實現(xiàn),提供了細粒度的訪問控制。通過權限和角色的組合,開發(fā)者可以非常靈活地控制訪問權限。

  1. 支持多種數(shù)據(jù)源
    Shiro 可以從多種數(shù)據(jù)源(如數(shù)據(jù)庫、LDAP、文件等)獲取用戶和權限信息,方便與各種現(xiàn)有系統(tǒng)整合。

  1. 支持 Web 和非 Web 環(huán)境
    Shiro 不僅可以在 Web 應用中使用,也支持在桌面應用或微服務等環(huán)境中使用。

Shiro 的基本使用示例

光講概念不是 V 哥風格,接下來,通過一個典型的 Shiro 應用來了解一下如何使用,包含配置 SecurityManager、配置 Realm、進行認證和授權等步驟。

  1. 配置 Shiro 環(huán)境 可以通過 shiro.ini 文件配置 Shiro,也可以通過代碼進行配置。

   [main]
   # 配置 SecurityManager
   securityManager = org.apache.shiro.mgt.DefaultSecurityManager


   # 配置 Realm
   myRealm = com.wg.MyCustomRealm
   securityManager.realms = $myRealm

  1. 創(chuàng)建自定義 Realm

自定義 Realm 通過繼承 AuthorizingRealm 并實現(xiàn) doGetAuthenticationInfodoGetAuthorizationInfo 方法來提供用戶和權限數(shù)據(jù)。

   public class MyCustomRealm extends AuthorizingRealm {
       @Override
       protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
           // 獲取用戶名和密碼等信息,查詢數(shù)據(jù)庫進行認證
           return new SimpleAuthenticationInfo(username, password, getName());
       }


       @Override
       protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
           // 獲取用戶角色和權限信息
           SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
           info.addRole("admin");
           info.addStringPermission("user:read");
           return info;
       }
   }

  1. 使用 Shiro 進行認證和授權

   Subject currentUser = SecurityUtils.getSubject();
   if (!currentUser.isAuthenticated()) {
       UsernamePasswordToken token = new UsernamePasswordToken("username", "password");
       try {
           currentUser.login(token);
           System.out.println("認證成功");
       } catch (AuthenticationException ae) {
           System.out.println("認證失敗");
       }
   }


   // 檢查權限
   if (currentUser.hasRole("admin")) {
       //用輸出模擬一下哈
       System.out.println("用戶擁有 admin 角色");
   }
   if (currentUser.isPermitted("user:read")) {
       //用輸出模擬一下哈
       System.out.println("用戶具有 user:read 權限");
   }

通過這個簡單的案例學習,咱們可以了解 Shiro 的基本使用,但這不是全部,聽V哥繼續(xù)慢慢道來。

場景案例

這點很重要,強調(diào)一下哈,Shiro 適合需要簡潔易用、安全控制要求靈活的 Java 應用,如中小型 Web 應用、桌面應用、分布式微服務等。對于大型企業(yè)應用或需要集成多種認證方式(如 OAuth2、JWT 等)的項目,Spring Security 可能會更合適。

要在微服務架構中實現(xiàn)基于 Apache Shiro 的安全認證和授權,比如一個訂單管理系統(tǒng)為例。這個系統(tǒng)包含兩個主要服務:

  1. 用戶服務:負責用戶的注冊、登錄、認證等操作。
  2. 訂單服務:允許用戶創(chuàng)建、查看、刪除訂單,并限制訪問權限。

咱們來看一下,這個應該怎么設計呢?

微服務案例設計

在這個場景中,我們需要以下幾項核心功能:

  1. 用戶認證:用戶通過用戶名和密碼登錄。
  2. 權限控制:僅管理員能刪除訂單,普通用戶只能查看和創(chuàng)建訂單。
  3. Token機制:使用 JWT Token(JSON Web Token)來管理用戶的登錄狀態(tài),實現(xiàn)無狀態(tài)認證,使得在分布式環(huán)境下不依賴于單一會話。
  4. 跨服務認證:訂單服務在接收到請求時,檢查并驗證用戶的身份和權限。

架構和技術選型

  • Spring Boot:用于快速搭建微服務。
  • Shiro:實現(xiàn)認證、授權。
  • JWT:生成和驗證 Token,保持無狀態(tài)認證。
  • Spring Data JPA:訪問數(shù)據(jù)庫存儲用戶和訂單數(shù)據(jù)。

系統(tǒng)結構

+------------------+      +---------------------+
|   用戶服務        |      |     訂單服務        |
|                  |      |                     |
| 用戶注冊、登錄    |      |   查看、創(chuàng)建、刪除訂單|
+------------------+      +---------------------+
            |                    |
            |----用戶 Token ------|

步驟:實現(xiàn)微服務中的認證和授權

1. 引入必要的依賴

pom.xml 文件中,添加 Shiro、JWT 和 Spring Data JPA 等依賴:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.8.0</version>
</dependency>


<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

2. 配置 Shiro 與 JWT 過濾器

使用 Shiro 的自定義 JWT 過濾器實現(xiàn)無狀態(tài)認證,通過 Token 驗證用戶。

public class JwtFilter extends BasicHttpAuthenticationFilter {


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Authorization");


        if (StringUtils.isBlank(token)) {
            return false;
        }


        try {
            // 解析 JWT token
            JwtToken jwtToken = new JwtToken(token);
            getSubject(request, response).login(jwtToken);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

3. 實現(xiàn)自定義 Realm

自定義 Realm,從數(shù)據(jù)庫獲取用戶和角色信息,并使用 JWT Token 進行無狀態(tài)認證。

public class JwtRealm extends AuthorizingRealm {


    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String jwtToken = (String) token.getPrincipal();


        // 驗證 Token
        String username = JwtUtil.getUsernameFromToken(jwtToken);
        if (username == null) {
            throw new AuthenticationException("Token 無效");
        }


        // 查詢用戶
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new AuthenticationException("用戶不存在");
        }


        return new SimpleAuthenticationInfo(jwtToken, jwtToken, getName());
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = JwtUtil.getUsernameFromToken(principals.toString());


        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = userService.findByUsername(username);


        // 添加角色和權限
        authorizationInfo.addRole(user.getRole());
        authorizationInfo.addStringPermission(user.getPermission());


        return authorizationInfo;
    }
}

4. 創(chuàng)建 JWT 工具類

編寫一個工具類,用于生成和解析 JWT Token。

public class JwtUtil {


    //這里替換一下你自己的secret_key
    private static final String SECRET_KEY = "這里打碼了"; 


    public static String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }


    public static String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject();
    }


    public static boolean isTokenExpired(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
        return claims.getExpiration().before(new Date());
    }
}

5. 編寫用戶服務和訂單服務接口

用戶服務接口

用戶服務提供注冊和登錄 API。

@RestController
@RequestMapping("/user")
public class UserController {


    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody User user) {
        userService.save(user);
        return ResponseEntity.ok("用戶注冊成功");
    }


    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user) {
        User dbUser = userService.findByUsername(user.getUsername());
        if (dbUser != null && dbUser.getPassword().equals(user.getPassword())) {
            String token = JwtUtil.generateToken(user.getUsername());
            return ResponseEntity.ok(token);
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("登錄失敗");
    }
}

訂單服務接口

訂單服務在操作訂單時會驗證用戶的角色和權限。

@RestController
@RequestMapping("/order")
public class OrderController {


    @GetMapping("/{orderId}")
    public ResponseEntity<?> getOrder(@PathVariable Long orderId) {
        Subject currentUser = SecurityUtils.getSubject();
        if (currentUser.isPermitted("order:read")) {
            // 查詢訂單
            return ResponseEntity.ok("訂單詳情");
        }
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body("無權限查看訂單");
    }


    @DeleteMapping("/{orderId}")
    public ResponseEntity<?> deleteOrder(@PathVariable Long orderId) {
        Subject currentUser = SecurityUtils.getSubject();
        if (currentUser.hasRole("admin")) {
            // 刪除訂單
            return ResponseEntity.ok("訂單已刪除");
        }
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body("無權限刪除訂單");
    }
}

最后

這個案例中咱們通過如何使用 Shiro、JWT 和 Spring Boot 來構建一個無狀態(tài)的微服務認證授權機制。通過 Shiro 實現(xiàn)用戶認證和權限控制,使用 JWT 實現(xiàn)無狀態(tài) Token 驗證。在輕量級的分布式微服務應用中,是不是使用 Shiro 感覺更加清爽呢,歡迎評論區(qū)一起討論,關注威哥愛編程,愛上Java,一輩子。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號