1. 程式人生 > >Shiro之學習筆記(六)

Shiro之學習筆記(六)

Shiro整合SpringMVC在Web環境下實現登入認證

Web環境下實現認證的基本流程: 1、jsp頁面:包含使用者資訊,並封裝到form表單中; 2、Spring MVC控制器:處理使用者請求:    - 獲取使用者的登入資訊        - shiro API來完成使用者認證     1)獲取Subject型別的例項     2)判斷使用者是否已經登入     3)使用UsernamePasswordToken物件來封裝使用者名稱和密碼     4)使用Subject中的login(token)方法     5)Realm:從資料庫中獲取安全資料        - 將token的型別轉換為UsernamePasswordToken        - 從轉換好的物件中獲取使用者名稱即可        - 查詢資料庫,從資料庫中查詢是否存在相對應的使用者名稱和密碼        - 如果查詢到了,就封裝結果,返回給我們的呼叫        - 如果沒有查詢到,就丟擲一個異常

springmvc-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置包掃描器,掃描@Controller註解的類 -->
   	<context:component-scan base-package="com.shiro.controller" />
   	<!-- 載入註解驅動 -->
   	<mvc:annotation-driven />
   	<!-- 配置檢視解析器 -->
   	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   	    <property name="prefix" value="/" />
   	    <property name="suffix" value=".jsp" />
   	</bean>
    <!-- 開啟aop,對類代理 -->
	<aop:config proxy-target-class="true"></aop:config>
	
</beans>

  login.jsp(包含使用者資訊,並封裝到form表單中):

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="login.action" method="post">
		username:<input type="text" name="username" />
		<br />
		password:<input type="password" name="password" />
		<br />
		<input type="submit" value="登入" />
	</form>
</body>
</html>

配置applicationContext.xml中的請求攔截路徑:

LoginController.java:

package com.shiro.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class LoginController {
	
	@RequestMapping("/login.action")
    public String Login(@RequestParam("username") String username,@RequestParam("password") String password) {
    	
    	//獲取Subject型別的例項
		System.out.println(username+"--------"+password);
    	Subject currentUser = SecurityUtils.getSubject();
    	//判斷使用者是否已經登入
    	if(currentUser.isAuthenticated()==false) {
    		//使用UsernamePasswordToken物件來封裝使用者名稱和密碼
    		UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    		//使用Subject中的login(token)方法
    		try{
    			currentUser.login(token);
    			System.out.println("登入驗證");
    		}catch(AuthenticationException e) {
    			return "error";
    		}
    	}
    	
    	return "index";
    }
}

index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<p>Index Page.</p>
	<a href="logout">LOGOUT</a><!--點選按鈕登出使用者並退出 -->
</body>
</html>

error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h3>Error Page.</h3>
</body>
</html>

ShiroRealm.java

package com.shiro.realm;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

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

//Realm:從資料庫中獲取並處理安全資料
public class ShiroRealm extends AuthenticatingRealm {
    
	/*
	 * 可以使用AuthenticationInfo介面中的SimpleAuthenticationInfo實現類來封裝正確的使用者名稱和密碼
	 * 
	 * doGetAuthenticationInfo方法:獲取認證資訊,如果資料庫中沒有資料,就返回null;反之,則返回指定的型別的物件
	 * 
	 * AuthenticationToken token:就是我們要認證的token
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		/*
		 * 將token的型別轉換為UsernamePasswordToken 
		 * 從轉換好的物件中獲取使用者名稱即可
		 * 查詢資料庫,從資料庫中查詢是否存在相對應的使用者名稱和密碼
		 * 如果查詢到了,就封裝結果,返回給我們的呼叫
		 * 如果沒有查詢到,就丟擲一個異常
		 */
		SimpleAuthenticationInfo info = null;
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		String userName = upToken.getUsername();
		
		//使用jdbc原生的API來從資料庫中查詢資料
		try {
			//獲取驅動
			Class.forName("com.mysql.jdbc.Driver");
			String url = "jdbc:mysql://localhost:3306/test";
			String username = "root";
			String password = "123";
			Connection con = DriverManager.getConnection(url, username, password);
			PreparedStatement ps = con.prepareStatement("select * from sec_user where user_name=?");
			ps.setString(1, userName);
			
			ResultSet rs = ps.executeQuery();
			
			if(rs.next()) {
				Object principal = userName;
				Object credentials = rs.getString(3);
				String realmName = this.getName();
				info = new SimpleAuthenticationInfo(principal, credentials, realmName);
			}else {
				throw new AuthenticationException();
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return info;
	}

	

}

建立資料表並插入資料:

CREATE TABLE `sec_user` (
  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
  `password` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `created_time` datetime DEFAULT NULL,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

insert into sec_user(user_name,password) values ('tom','123456');
insert into sec_user(user_name,password) values ('jim','123456');