Shrio功能應用(一)--登陸驗證(源碼)以及權限執行過程

大致過程

     用前臺的用戶名、明文密碼等信息創建驗證的token令牌對象
     執行subject.login(token);登陸時,會跳轉到自定義的Realm的認證方法,一般在認證方法裏面查詢數據庫,獲取數據庫對應的user信息(包括加密後的密碼),將user信息封裝到info對象裏
     Shiro內部驗證的時候,會將token的明文密碼加密後,與info裏user的密碼(也就是數據庫中用戶密碼)進行比較,結果一致則返回true,登陸成功

執行過程

     1 .前臺頁面登陸後,執行登陸驗證方法subject.login(token);實際調用的是securityManager.login(this, token)
在這裏插入圖片描述
     1-1.securityManager.login(this, token)方法裏調用 authenticate(token);方法獲取需要匹配的AuthenticationInfo info對象
在這裏插入圖片描述
     1-2.authenticate方法裏調用doAuthenticate(token);來獲取需要匹配的AuthenticationInfo info對象
在這裏插入圖片描述
     1-3.在doAuthenticate方法裏面,會獲取自定義的Realm,本例只設置一個Realm,所以跳轉單Realm的認證方法doSingleRealmAuthentication
在這裏插入圖片描述
     1-4.在doSingleRealmAuthentication方法裏,會跳轉getAuthenticationInfo方法:
在這裏插入圖片描述
     1-5.在getAuthenticationInfo方法中,先從緩存中獲取匹配的AuthenticationInfo info對象,本例沒有設置緩存,所以往下通過自定義Realm的doGetAuthenticationInfo(token)方法獲取info對象:
在這裏插入圖片描述
     1-6.往下會跳轉到自定義Realm重寫的doGetAuthenticationInfo方法裏。方法返回SimpleAuthenticationInfo的info對象,SimpleAuthenticationInfo(,,,,)有四個參數:
          1).第一個Object principal,可以是用戶名,也可以是user對象
          2).第二個Object hashedCredentials,需要認證的密碼,前臺輸入的密碼會和此密碼匹配
          3).第三個ByteSource credentialsSalt,加密認證是的salt
          4).第四個String realmName,就是realm的名字,即shiroConfig配置的relam
在這裏插入圖片描述
     1-7.執行完自定義Realm獲得info對象,info對象包含了數據庫的密碼(加密過的密碼),然後執行assertCredentialsMatch(token, info);進行密碼驗證:
在這裏插入圖片描述
     1-8.進入密碼驗證方法assertCredentialsMatch先獲取密碼比較器,然後再調用密碼比較方法doCredentialsMatch:
在這裏插入圖片描述
     1-9.在doCredentialsMatch方法裏,hashProvidedCredentials(token, info);方法主要是將前臺的明文密碼通過加密算法SimpleHash後,獲取加密後的密碼。
在這裏插入圖片描述
在這裏插入圖片描述
     1-10.前臺明文密碼加密後密碼tokenHashedCredentials與自定義Realm從數據庫中獲取的加密密碼進行匹配:
在這裏插入圖片描述
     1-11.匹配成功subject.login(token);返回true;
在這裏插入圖片描述
     2.登陸成功重定向到userList.do的控制器,進行成功頁面跳轉:
在這裏插入圖片描述
     2-1.userList頁面中使用了shiro:hasPermission標籤做權限控制,頁面有三個shiro:hasPermission權限標籤,則會執行三次自定義Realm裏的授權方法doGetAuthorizationInfo:
在這裏插入圖片描述
     2-2.具體源碼沒怎麼看,但主要和驗證差不多,都是先從緩存取授權的info對象,沒有再跑自定義Realm的授權方法去獲取info對象。我想大概就是shiro:hasPermission標籤的值和查詢數據庫獲取的權限列表perms進行匹配,不涉及加密,匹配成功則顯示標籤內容:
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

代碼示例

      代碼地址:
          https://github.com/OooooOz/SpringBoot-Shiro

      前臺登陸頁面:

<body>
	<h2>登錄頁面</h2>
	<form action="login.do" method="post" name="loginForm">
		用戶名稱:<input type="text" name="username" value=""><br>
		用戶密碼:<input type="password" name="password" value=""><br>
		<input type="submit" name="log" value="登錄"/>
		<input type="checkbox" name="rememberMe" />記住我<br/>
	</form>
	<a href="/unlockAccount.do">解鎖admin用戶</a></button>
	<div th:text="${msg}"></div>
</body>

     控制器Controller:

@Controller
public class LoginController {

	@Resource
	IUserService userService;

	@RequestMapping("/toLogin.do")
	public String toLogin() {
		return "login";
	}

	@RequestMapping("/login.do")
	public String login(String username, String password,boolean rememberMe, Model model) {
		// 判斷用戶名密碼是否爲空
		if (username != null && !"".equals(username) && password != null && !"".equals(password)) {
			// 1.獲取subject
			Subject subject = SecurityUtils.getSubject();
			// 2.創建驗證用的令牌對象
			UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
			try {
				//3.登陸認證
				subject.login(token);
				boolean flag = subject.isAuthenticated();	//判斷是否通過認證
				//4.認證結果處理
				if(flag){
					System.out.println("登錄成功!");
					User user = (User)subject.getPrincipal();
					model.addAttribute("user",user);
					return "redirect:userList.do";
				}else{
					model.addAttribute("msg","登錄認證失敗!");
					return "login";
				}
			
			} catch (Exception e) {
				model.addAttribute("msg","登錄認證失敗!");
				if (e instanceof LockedAccountException) {
					model.addAttribute("msg","失敗次數過多,用戶已鎖定,五分鐘後再試");
				}
			}
		}
		return "login";
	}
	@RequestMapping("/userList.do")
	//@ResponseBody
	public String userList(Model model){
		//查詢所有的用戶信息並且顯示到頁面上
		Subject subject = SecurityUtils.getSubject();
		System.out.println("==============是否通過認證:"+subject.isAuthenticated());
		System.out.println("==============是否記住我:"+subject.isRemembered());
		List<User> list = userService.findAll();
		model.addAttribute("userList", list);
		return "userList";
	}
	@RequestMapping("/unFunc.do")
	public String noFunc(){
		return "unFunc";
	}

	@RequestMapping("/logout.do")
	public String logout(){
		//清除session
		return "login";
	}
}

     ShiroConfig的過濾器:

	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
        //loginUrl認證提交地址,如果沒有認證將會請求此地址進行認證,請求此地址將由formAuthenticationFilter進行表單認證
		shiroFilterFactoryBean.setLoginUrl("/toLogin.do");
        //通過unauthorizedUrl指定沒有權限操作時跳轉頁面
		shiroFilterFactoryBean.setUnauthorizedUrl("/unFunc.do");

		//-----------------------------過慮器鏈定義------------------------------//
        LinkedHashMap<String, String> perms = new LinkedHashMap<>();
        perms.put("/login.do","anon");
        //其他資源都需要認證  authc 表示需要認證才能進行訪問 user表示配置記住我或認證通過可以訪問的地址
        perms.put("/*", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(perms);  //把權限過濾map設置shiroFilterFactoryBean
		return shiroFilterFactoryBean;
	}

     自定義Realm:

	/**
	 * 認證方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("執行了認證方法。。。。");
		String username = token.getPrincipal().toString();
		System.out.println("username:" + username);
		//需要通過用戶名查詢用戶密碼
		User user = userService.findByUsername(username);
		//把user對象封裝的AuthenticationInfo中返回
		SimpleAuthenticationInfo authenticationInfo = 
				new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),"shiroRealm");
		return authenticationInfo;
	}
	

     登陸成功跳轉頁面:

<table>
	<caption>
		<div shiro:hasPermission="function:add"><a href="/toAdd.do">添加</a></div>
		<div shiro:hasPermission="function:update"><a href="/toUpdate.do">修改</a></div>
		<div shiro:hasPermission="function:delete"><a href="/toDelete.do">刪除</a></div>
	</caption>
	<tr>
		<td>用戶ID</td><td>用戶名稱</td><td>電話</td><td>email</td><td>狀態</td><td>創建時間</td><td>備註</td>
	</tr>
	<tr th:each="user : ${userList}">
		<td th:text="${user.userId}"></td>
		<td th:text="${user.userName}"></td>
		<td th:text="${user.phone}"></td>
		<td th:text="${user.email}"></td>
		<td th:if="${user.status == 1 ?'正常':'異常'}"></td>
		<td th:text="${user.createTime}"></td>
		<td th:text="${user.note}"></td>
	</tr>
</table>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章