Shiro——授权

在实际项目中,每一个用户角色登录进系统看到的菜单可能会不一样,这样就涉及到了用户权限的问题,Shiro也是目前常用的权限框架。

源码下载

一、授权方式

shiro支持三种授权方式:

1、编程式(基本不用):通过写if/else授权代码块完成。

2、注解式:通过在执行的Java方法上防止相应的注解完成,没有权限将抛出相应的异常。

3、JSP标签:在JSP页面通过相应的标签完成。

二、默认拦截器

1、rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

2、port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

3、perms:例子/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

4、roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

5、anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

6、authc:例如/admins/user/**=authc表示需要认证才能使用,没有参数

7、authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

8、ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

9、user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

这些过滤器分为两组,一组是认证过滤器,一组是授权过滤器。其中anon,authcBasic,auchc,user是第一组,

perms,roles,ssl,rest,port是第二组。

三、Shiro标签

使用shiro标签之前要在jsp中引入标签库
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>

shiro的几种标签:
shiro:guest:用户没有身份验证时显示的信息 ,即游客访问信息
shiro:user:用户登陆后显示的信息,通过RememberMe认证通过
shiro:principal:显示用户身份信息
shiro:authenticated:用户已经身份验证通过,即通过Subject.login登录成功,不是RememberMe登录的
shiro:notAuthenticated:用户未进行身份验证,即没有通过Subject.login登录,包括RememberMe自动登录的也属于未进行身份验证
shiro:hasRole name="admin":如果当前Subject有角色,将显示body体内容
shiro:hasAnyRoles name="admin,user":只要有body体内有任意一个角色,就会显示里面的内容
shiro:lacksRole name="admin":如果当前的Subject没有角色将显示标签内内容 
shiro:hasPermission name="user:create":如果当前Subject有权限就显示标签里的内容 
shiro:lacksPermission name="org:create":如果当前Subject没有权限将显示标签里的内容

四、权限注解

权限注解可以用在service和controller层
1、@RequiresAuthentication:表示当前Subject已经通过login进行了身份验证;即Subject.isAuthenticated()返回true
2、@RequiresUser:表示当前Subject已经身份验证或者通过RemmberMe登录的
3、@RequiresGuest:表示当前Subject没有身份验证或通过RemmberMe登录过,即游客身份
4、@RequiresRole(value={"admin","user"},logical=Logical.AND):表示当前Subject需要角色admin和Uuser
5、RequiresPermissions(value={"user:a","user:b"},logical=Logical.OR):表示当前Subject需要权限user:a或user:b

五、具体实现

1、在web.xml中配置Spring、springmvc、shiro。



  shiro-2
  
    index.jsp
  
  
  
  
  	contextConfigLocation
  	classpath:applicationContext.xml
  
  
  	org.springframework.web.context.ContextLoaderListener
  
  
  
  
  	spring
  	org.springframework.web.servlet.DispatcherServlet
  	
       contextConfigLocation
       classpath:spring-servlet.xml
    
  	1
  
  
  	spring
  	/
  
  
  
  
      shiroFilter
      org.springframework.web.filter.DelegatingFilterProxy
      
          targetFilterLifecycle
          true
      
  

  
      shiroFilter
      /*
  

2、配置springmvc配置文件:spring-servlet.xml



	
	
	
	
	
	
	
	
	
	
		
		
	
	

3、配置IOC容器:applicationContext.xml



	
    
    
        
        
        
        	
    			
    			
    		
        
        

	
    
    
        
        
        
        	
    			
    			
    		
        
        
        
        
    

    
    
        
    

    
    
    	
    	
    		
    	
    
    
    	
    		
    			
    			
    		
    	
    
    
    	
    		
    			
    			
    		
    	
    

    
    

    
    
    
        
    

    
    
        
        
        
        
        
        
        
        
         
    
	
	
	
	
		
    
	

    

    
    
        
    

    
    
    	
    	
    		
    	
    
    
    	
    		
    			
    			
    		
    	
    
    
    	
    		
    			
    			
    		
    	
    

    
    

    
    
    
        
    

    
    
        
        
        
        
        
        
        
        
         
    
	
	
	
	
		
    
	

4、创建Realm并要在Spring IOC容器中配置,这里会用了两个Realm,当然可以使用更多个。Realm认证策略参照:Shiro——认证

package com.mfc.realm;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

/**
 * @author 74790
 * 1.授权需要继承AuthorizingRealm,并实现doGetAuthorizationInfo 方法
   2.AuthorizingRealm类继承自AuthenticatingRealm,但没有实现AuthenticatingRealm中的
	 doGetAuthenticationInfo,所以认证和授权只需要继承AuthorizingRealm就可以了,同时实现他的
	   两个抽象方法。
 */
public class ShiroRealm extends AuthorizingRealm {
	
	// 认证的方法
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("[ShiroRealm] doGetAuthenticationInfo");

		//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
		//以下信息是从数据库中获取的
		//principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
		Object principal = username;
		//credentials:密码
		Object credentials = null;
		if("admin".equals(username)){
			credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
		}else if("user".equals(username)){
			credentials = "098d2c478e9c11555ce2823231e02ec1";
		}
		//realmName:当前realm 对象的name,调用父类的getName()方法即可
		String realmName = getName();
		//盐值,防止两用户密码一样的时候不安全
		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		return info;
	}
	
	// 授权需要实现的方法
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//1.从PrincipalCollection中获取登陆用户的信息
		Object principal = principals.getPrimaryPrincipal();
		
		//2.利用登陆用户的信息来获取当前用户的角色和权限(可能需要查询数据库)
		Set roles = new HashSet();
		roles.add("user");
		if("admin".equals(principal)){
			roles.add("admin");
		}
		
		//3.创建SimpleAuthorizationInfo对象,并设置其roles
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		
		//4.返回SimpleAuthorizationInfo对象
		return info;
	}
	
	public static void main(String[] args) {
		String hashIterations = "MD5";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("user");;
		int algorithmName = 1024;
		
		Object result = new SimpleHash(hashIterations, credentials, salt, algorithmName);
		System.out.println("result:" + result);
	}
	

}
package com.mfc.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;

/**
 * @author 74790
 * 认证的Realm
 */
public class SecondRealm extends AuthenticatingRealm {

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("[SecondRealm] doGetAuthenticationInfo");
		//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
		//以下信息是从数据库中获取的
		//principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
		Object principal = username;
		//credentials:密码
		//Object credentials = "fc1709d0a95a6be30bc5926fdb7f22f4";
		Object credentials = null;
		if("admin".equals(username)){
			credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";
		}else if("user".equals(username)){
			credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";
		}
		//realmName:当前realm 对象的name,调用父类的getName()方法即可
		String realmName = getName();
		//SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
		//盐值
		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		return info;
	}
	
	public static void main(String[] args) {
		String hashIterations = "SHA1";
		Object credentials = "123456";
		Object salt = ByteSource.Util.bytes("admin");;
		int algorithmName = 1024;
		
		Object result = new SimpleHash(hashIterations, credentials, salt, algorithmName);
		System.out.println("result:" + result);
	}
	

}

5、测试注解配置权限的service:关于注解的使用可以看上面的四

package com.mfc.service;

import java.util.Date;

import org.apache.shiro.authz.annotation.RequiresRoles;

/**
 * @author 74790
 * Shiro的注解方式配置在service或者是Controller中
 */
public class ShiroService {
	/*
	 * 只有admin角色才可以访问这个方法
	 * */
	@RequiresRoles({"admin"})
	public void test(){
		System.out.println("ShiroService测试权限:" + new Date());
	}
}

6、Controller:

package com.mfc.ctrl;

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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.mfc.service.ShiroService;

@Controller
@RequestMapping(value = "shiroCtrl")
public class ShiroCtrl {
	
	@Autowired
	private ShiroService shiroService;
	
	@RequestMapping(value = "test")
	public String test(){
		shiroService.test();
		return "redirect:/list.jsp";
	}
	
	@RequestMapping(value = "login")
	public String login(@RequestParam("username")String username, 
			@RequestParam("password")String password, Model model){
		Subject currentUser = SecurityUtils.getSubject();
		
        // 测试当前的用户是否已经被认证,即是否已经登录
        // 调用Subject 的isAuthenticated()
        if (!currentUser.isAuthenticated()) {
        	// 把用户名和密码封装为UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // RememberMe
            token.setRememberMe(true);
            try {
            	// 执行登录
            	// 这里的token其实传到了ShiroRealm.doGetAuthenticationInfo(AuthenticationToken token)
                currentUser.login(token);
            }catch (AuthenticationException ae) {
            	// 所有认证时异常的父类
            	System.err.println("登录失败:"+ae.getMessage());
            }
        }
		return "redirect:/list.jsp";
	}
}

7、从数据库中获取权限配置资源的bean:这个bean需要在Spring IOC容器中注入,可以查看上面的applicationContext.xml配置文件。

package com.mfc.factory;

import java.util.LinkedHashMap;

/**
 * @author 74790
 * 使用bean配置的方式,将权限配置配置到数据库里面去
 */
public class FilterChainDefinitionMapBuilder {

	public LinkedHashMap buildFilterChainDefinitionMapBuilder(){
		LinkedHashMap map = new LinkedHashMap();
		
		/*
		 *
		这些可以存到数据库里面去,在这里取出来即可,注意放进map的顺序
		/login.jsp = anon
	    /shiroCtrl/login = anon
	    /shiroCtrl/logout = logout
	    
	    /user.jsp = roles[user]
	    /admin.jsp = roles[admin]
	    # everything else requires authentication:
	    /** = authc
		 * */
		map.put("/login.jsp", "anon");
		map.put("/shiroCtrl/login", "anon");
		map.put("/shiroCtrl/logout", "logout");
		map.put("/user.jsp", "roles[user]");
		map.put("/admin.jsp", "roles[admin]");
		map.put("/**", "authc");
		return map;
	
	}
}

8、JSP代码这里省略。

 

你可能感兴趣的:(shiro)