天天看點

4.Shiro認證分析和流程及實作

1.shiro認證的全過程

1. 擷取目前的 Subject. 調用 SecurityUtils.getSubject();

2. 測試目前的使用者是否已經被認證. 即是否已經登入. 調用 Subject 的 isAuthenticated() 

3. 若沒有被認證, 則把使用者名和密碼封裝為 UsernamePasswordToken 對象

   1). 建立一個表單頁面

   2). 把請求送出到 SpringMVC 的 Handler

   3). 擷取使用者名和密碼. 

4. 執行登入: 調用 Subject 的 login(AuthenticationToken) 方法. 

5. 自定義 Realm 的方法, 從資料庫中擷取對應的記錄, 傳回給 Shiro.

   1). 實際上需要繼承 org.apache.shiro.realm.AuthenticatingRealm 類

   2). 實作 doGetAuthenticationInfo(AuthenticationToken) 方法. 

6. 由 shiro 完成對密碼的比對. 

2.shiro認證Realm部分代碼

(1)表單(這裡表單url請求必須要有權限,沒有就去application.xml設定:/shiro/login = anon)

<form action="shiro/login" method="post">
   
       使用者名:<input type="text" name="username"/>
       <br><br>
        密碼:	<input type="password" name="password">
        <br><br>
        <input type="submit" value="submit" />
   </form>
           

(2)背景controller

package com.yang.shiro.realm;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/shiro")
public class ShiroHandle {

	private static final transient Logger log = LoggerFactory.getLogger(ShiroHandle.class);
	
	@RequestMapping("/login")
	public String login(@RequestParam("username") String username,
			@RequestParam("password") String password) {
		
		 // 擷取目前的 Subject. 調用 SecurityUtils.getSubject();
        Subject currentUser = SecurityUtils.getSubject();
     // let's login the current user so we can check against roles and permissions:
        // 測試目前的使用者是否已經被認證. 即是否已經登入. 
        // 調動 Subject 的 isAuthenticated() 
        if (!currentUser.isAuthenticated()) {
        	// 把使用者名和密碼封裝為 UsernamePasswordToken 對象
            UsernamePasswordToken token = new UsernamePasswordToken("username", "password");
            // rememberme
            token.setRememberMe(true);
            try {
            	System.out.println("1:"+token.hashCode());
            	// 執行登入. 
                currentUser.login(token);
            } 
            // 所有認證時異常的父類. 
            catch (AuthenticationException ae) {
                System.out.println("登入失敗!");
            }
        }
		return "redirect:/list.jsp";
		
	}
}
           

3.把送出的token傳遞給Realm

package com.yang.shiro.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.AuthenticatingRealm;

public class ShiroRealm extends AuthenticatingRealm {

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// TODO Auto-generated method stub
		System.out.println("2:"+token.hashCode());
		System.out.println("token:"+token);
		//1.把AuthenticationToken 轉換為 UsernamePasswordToken
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		//2.從 UsernamePasswordToken 中來擷取username
		String username = upToken.getUsername();
		//3.調用資料庫的方法從資料庫中查詢 username 對應的使用者記錄,這裡就不擷取了
		System.out.println("從資料庫中擷取username:" + username + "所對應的使用者資訊。");
		//4.若使用者資訊不存在,則可抛出 UnknownAccountException 異常
		if("unknown".equals(username)) {
			throw new UnknownAccountException("使用者不存在!");
		}
		//5.根據使用者資訊的情況,決定是否需要抛出其它的  AuthenticationException  異常
		if("monster".equals(username)) {
			throw new LockedAccountException("使用者被鎖定!");
		}
		//6.根據使用者的情況,來建構 AuthenticationInfo 對象并傳回,通常使用的實作類:SimpleAuthenticationInfo
		//以下資訊是從資料庫中擷取的。
		//(1)principal:認證的實體資訊。可以是username,也可以是資料表對應的使用者的實體類對象
		Object principal = username;
		//(2)credentials:密碼
		Object credentials = "123456";
		//(3)realmName:目前realm對象的name,調用父類的getName()方法即可
		String realmName = getName();
		System.out.println("realmName:" + realmName);
		SimpleAuthenticationInfo  info = new SimpleAuthenticationInfo(principal, 
                                                              credentials, realmName); 
		return info;
	}
}
           

繼續閱讀