程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Shiro權限認證

Shiro權限認證

編輯:JAVA綜合教程

Shiro權限認證


一、權限認證核心要素

權限認證顧名思義,就是在應用系統中,控制誰能訪問哪些資源。核心要素有仨:權限、角色、用戶

\

權限:即操作資源的權利,如訪問某個url,對某個模塊數據進行增刪改查

角色:權限的集合,一種角色可以包含多種權限。例如操作員角色可查看系統賬單、進行結賬操作多種權限。

用戶:也就是身份認證中提到的subject一角。

二、授權

shiro授權的方式通常有三種:

1、編程式授權:在代碼中進行授權操作,可基於角色和權限兩種方式。

2、注解式授權:使用注解對方法或類進行授權,標注該類可被哪些權限、角色所使用。

3、Jsp標簽授權:shiro比較靈活的地方筆者覺得就是jsp標簽授權,通過shiro的guest、user、principal等標簽,可通過訪問權限的不同,控制頁面信息顯示。免去了一大部分後台處理邏輯。好方便,好好用。後面會有詳細介紹。

三、編程式授權實例

1、同樣首先創建ini文件

[users]
java1234=123456,role1,role2
jack=123,role1

這是一個通過角色授權的方式,具體含義是指:用戶名為java1234的用戶享有role1,role2角色的權利,jack用戶享有role1的權利。

2、java代碼通過hasRole 和checkRole的方法可判斷某用戶是否具有role1、role2角色權利。

這裡首先將一些shiro初始化、預處理的操作封裝成一個util類

public class ShiroUtil {
	public static Subject login(String configFile,String userName,String password){
		// 讀取配置文件,初始化SecurityManager工廠
		Factory factory=new IniSecurityManagerFactory(configFile);
		// 獲取securityManager實例
		SecurityManager securityManager=factory.getInstance();
		// 把securityManager實例綁定到SecurityUtils
		SecurityUtils.setSecurityManager(securityManager);
		// 得到當前執行的用戶
		Subject currentUser=SecurityUtils.getSubject();
		// 創建token令牌,用戶名/密碼
		UsernamePasswordToken token=new UsernamePasswordToken(userName, password);
		try{
			// 身份認證
			currentUser.login(token);	
			System.out.println("身份認證成功!");
		}catch(AuthenticationException e){
			e.printStackTrace();
			System.out.println("身份認證失敗!");
		}
		return currentUser;
	}
}
新建roleTest類,通過調用user的hasRole、checkRole方法判斷用戶權限。

public class RoleTest {

	@Test
	public void testHasRole() {
		Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "java1234", "123456");
		// Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
		System.out.println(currentUser.hasRole("role1")?"有role1這個角色":"沒有role1這個角色");
		boolean []results=currentUser.hasRoles(Arrays.asList("role1","role2","role3"));
		System.out.println(results[0]?"有role1這個角色":"沒有role1這個角色");
		System.out.println(results[1]?"有role2這個角色":"沒有role2這個角色");
		System.out.println(results[2]?"有role3這個角色":"沒有role3這個角色");
		System.out.println(currentUser.hasAllRoles(Arrays.asList("role1","role2"))?"role1,role2這兩個角色都有":"role1,role2這個兩個角色不全有");
		
		currentUser.logout();
	}

	@Test
	public void testCheckRole() {
		Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "java1234", "123456");
		// Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
		currentUser.checkRole("role1");
		currentUser.checkRoles(Arrays.asList("role1","role2"));
		currentUser.checkRoles("role1","role2","role3");
		
		currentUser.logout();
	}
}

通過權限permission的權限驗證方式如下:

[users]
java1234=123456,role1,role2
jack=123,role1
[roles]
role1=user:select
role2=user:add,user:update,user:delete

這裡就補充了role1、role2用戶具體具有哪些操作權,role1可進行select操作,同理,role2可進行增刪改操作。同樣調用user關於permission認證的方法,可對用戶具體操作權限進行認證。

public class PermissionTest {

	@Test
	public void testIsPermitted() {
		Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "java1234", "123456");
		// Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
		System.out.println(currentUser.isPermitted("user:select")?"有user:select這個權限":"沒有user:select這個權限");
		System.out.println(currentUser.isPermitted("user:update")?"有user:update這個權限":"沒有user:update這個權限");
		boolean results[]=currentUser.isPermitted("user:select","user:update","user:delete");
		System.out.println(results[0]?"有user:select這個權限":"沒有user:select這個權限");
		System.out.println(results[1]?"有user:update這個權限":"沒有user:update這個權限");
		System.out.println(results[2]?"有user:delete這個權限":"沒有user:delete這個權限");
		System.out.println(currentUser.isPermittedAll("user:select","user:update")?"有user:select,update這兩個權限":"user:select,update這兩個權限不全有");
		
		currentUser.logout();
	}

	@Test
	public void testCheckPermitted() {
		Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "java1234", "123456");
		// Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
		currentUser.checkPermission("user:select");
		currentUser.checkPermissions("user:select","user:update","user:delete");
		currentUser.logout();
	}
}

四、shiro與web集成進行權限認證

下面介紹在java web程序中使用Shiro進行權限認證。

  1. 首先也是添加shiro相關jar包:shiro-web、shiro-core;commons-logging、slf4j-api、log4j;jstl、javax.servlet.jsp-api、javax.servlet-api

  2. 在web.XML中添加shiroFilter過濾器,並初始化創建的shiro.ini配置文件。

      
    	    org.apache.shiro.web.env.EnvironmentLoaderListener
      
       
    	
    	
    	    ShiroFilter
    	    org.apache.shiro.web.servlet.ShiroFilter
    	
    	
    	
    	    ShiroFilter
    	    /*
    	
    	

    shiro.ini文件

    [main]
    authc.loginUrl=/login
    roles.unauthorizedUrl=/unauthorized.jsp
    perms.unauthorizedUrl=/unauthorized.jsp
    [users]
    java1234=123456,admin
    jack=123,teacher
    marry=234
    json=345
    [roles]
    admin=user:*
    teacher=student:*
    [urls]
    /login=anon
    /admin=authc
    /student=roles[teacher]
    /teacher=perms["user:create"]

    3.創建login和adminservlet,分別用於直接登陸轉發到login.jsp,和admin登錄進行身份驗證,轉發到succeess.jsp和error.jsp

    public class LoginServlet extends HttpServlet{
    	private static final long serialVersionUID = 1L;
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		System.out.println("login doget");
    		req.getRequestDispatcher("login.jsp").forward(req, resp);
    	}
    	@Override
    	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		System.out.println("login dopost");
    		String userName=req.getParameter("userName");
    		String password=req.getParameter("password");
    		Subject subject=SecurityUtils.getSubject();
    		UsernamePasswordToken token=new UsernamePasswordToken(userName, password);
    		try{
    			subject.login(token);	
    			resp.sendRedirect("success.jsp");
    		}catch(Exception e){
    			e.printStackTrace();
    			req.setAttribute("errorInfo", "用戶名或者密碼錯誤");
    			req.getRequestDispatcher("login.jsp").forward(req, resp);
    		}
    	}
    }

    4.配置數據源-這裡介紹兩種reaml:Text Reaml和自定義JDBC reaml 。

    Text Reaml 配置詳情與訪問流程

    text Reaml配置整合到Shiro.ini文件中,具體配置了四個用戶,java1234擁有admin角色權限,jack擁有teacher角色權限;角色admin可對user進行任意crud操作,teacher可對student進行crud操作;訪問的urls,請求/login地址時,這裡的anon是指游客身份,不需要進行任何身份認證;請求/admin地址時,需要進行身份認證,進行filter過濾後,跳轉到【main】中進行了配置為/login (jsp頁面),同樣,role和perms的權限認證頁設置為unauthorized.jsp;

    上面ini配置達到的效果就是:當請求訪問localhost:8080/shiro/login 時直接跳到hello頁面,無需進行身份驗證。當訪問localhost:8080/shiro/admin時,先轉發到login.jps進行身份驗證,進入adminServlet,驗證結束後轉發到error或succeess頁面。再次訪問admin地址時,由於第一次訪問記錄了用戶登錄信息,故無需在登陸直接跳轉到success頁面。而訪問localhost:8080/shiro/student時,login信息如果使用json(沒有任何角色權限的用戶),則因為該用戶權限不足(因為配置中訪問student需要teacher角色)直接跳轉到無權限訪問頁面。這就是使用 textReaml進行身份驗證和權限驗證的配置。

    自定義 JDBC Reaml的配置與訪問流程

    由於text Reaml的信息畢竟有限,配置也相對比較麻煩,所以一般應用程序使用的都是自定義reaml,此處創建一個自定義JDBC readml並演示reaml與java程序結合的流程。

    \

    由於需要創建數據庫(創建用戶、角色、權限三張表,依次主外鍵關聯),然後首先引入數據庫驅動jar包;在shiro,ini文件中指定當前securityManager使用的驗證策略是自定義jdbcReaml。

    [main]
    authc.loginUrl=/login
    roles.unauthorizedUrl=/unauthorized.jsp
    perms.unauthorizedUrl=/unauthorized.jsp
    myRealm=com.java.realm.MyRealm
    securityManager.realms=$myRealm
    [urls]
    /login=anon
    /admin*=authc
    /student=roles[teacher]
    /teacher=perms["user:create"]
    最後創建數據庫連接Util類和myReaml類調用底層數據查詢dao即可。

    /**
     * 數據庫工具類
     * @author 
     */
    public class DbUtil {
    	public Connection getCon() throws Exception{
    		Class.forName("com.mysql.jdbc.Driver");
    		Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/db_shiro", "root", "123456");
    		return con;
    	}
    	public void closeCon(Connection con)throws Exception{
    		if(con!=null){
    			con.close();
    		}
    	}
    	public static void main(String[] args) {
    		DbUtil dbUtil=new DbUtil();
    		try {
    			dbUtil.getCon();
    			System.out.println("數據庫連接成功");
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			System.out.println("數據庫連接失敗");
    		}
    	}
    }
    MyReaml類:繼承shiro AuthorizingReaml類,重寫身份驗證和權限驗證兩個方法。(這裡也就解釋了為什麼訪問student地址時,並未配置需要先登錄,程序卻自動跳轉到登錄面。因為底層封裝了默認請求都先進行身份認證的方法。)

    public class MyRealm extends AuthorizingRealm{
    	private UserDao userDao=new UserDao();
    	private DbUtil dbUtil=new DbUtil();
    	/**
    	 * 為當前登錄的用戶授予角色和權限
    	 */
    	@Override
    	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    		String userName=(String)principals.getPrimaryPrincipal();
    		SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
    		Connection con=null;
    		try{
    			con=dbUtil.getCon();
    			authorizationInfo.setRoles(userDao.getRoles(con,userName));
    			authorizationInfo.setStringPermissions(userDao.getPermissions(con,userName));
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			try {
    				dbUtil.closeCon(con);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    		return authorizationInfo;
    	}
    	/**
    	 * 驗證當前登錄的用戶
    	 */
    	@Override
    	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    		String userName=(String)token.getPrincipal();
    		Connection con=null;
    		try{
    			con=dbUtil.getCon();
    			User user=userDao.getByUserName(con, userName);
    			if(user!=null){
    				AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),"xx");
    				return authcInfo;
    			}else{
    				return null;
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			try {
    				dbUtil.closeCon(con);
    			} catch (Exception e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		return null;
    	}
    
    }

    這兩個方法分別封裝了一個身份信息實體類AuthenticationInfo 和AuthorizetionInfo返回。

    首先獲取用戶信息,根據用戶名查詢數據庫中的用戶、權限等其他信息驗證即可。整體流程是當浏覽器訪問例如:localhost:8080/shiro/admin 地址時,先調用loginServlet,在執行user.login方法時,進入自定義MyReaml類,獲取用戶信息,進行身份驗證。


    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved