shiro權限管理

1.記錄一下使用shiro作權限管理的過程,在此以前須要瞭解一下RBAC權限管理的模型,本人以前寫了一個半成品,有錯誤的那些類能夠刪除掉。項目地址:https://gitee.com/bruceT/sshcss

2.有關shiro的maven依賴前端

<!--shiro所需的jar包 version:1.2.2-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>${shiro.version}</version>
		</dependency>

3.首先先貼有關權限的實體代碼java

@Entity
@Table(name="sys_user")
@ExcelTarget("user")
public class SysUser {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(length = 16)
	private String name;
	
	@Column(length = 16)
	@Excel(name = "用戶名", width=25, orderNum = "1", needMerge = true)
	private String username;
	
	@Column(length = 16)
	@Excel(name = "密碼", orderNum = "2", needMerge = true)
	private String password;
	
	/** 刪除標記:0.未刪除(默認) 1.已刪除 */
	@Column(name = "delete_flag",length = 2, columnDefinition = "INT default 0")
	private Integer deleteFlag = 0;
	
	@ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "sys_user_role", joinColumns = { 
 @JoinColumn(name = "user_id") }, inverseJoinColumns = {@JoinColumn(name = "role_id") })
	private Set<SysRole> roles; // 一個用戶具備多個角色

	@Transient
	public Set<String> getRoleName(){
		Set<SysRole> roles = getRoles();
		Set<String> set = new HashSet<String>();
		Iterator<SysRole> it = roles.iterator();
		while(it.hasNext()){
			SysRole role = it.next();
			set.add(role.getRoleName());
		}
        return set;
	}
//省略get、set的方法
}
@Entity
@Table(name = "sys_role")
public class SysRole {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name="role_name")
    private String roleName;
    
    @Column(name="role_description")
    private String roleDescription;
    
	@ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "sys_role_permission", 
joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = {
            @JoinColumn(name = "permission_id") })
    private Set<SysPermission> permissions; // 一個角色對應多個權限
    
    @ManyToMany
    @JoinTable(name = "sys_user_role", 
joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = {@JoinColumn(name = "user_id") })
    private Set<SysUser> users; // 一個角色對應多個用戶
    
    @Transient
    public List<String> getPermissionsName() {
        List<String> list = new ArrayList<String>();
        Set<SysPermission> permissions = getPermissions();
        Iterator<SysPermission> it = permissions.iterator();
        while(it.hasNext()){
        	SysPermission permission = it.next();
        	list.add(permission.getPermissionName());
        }
        return list;
    }
//省略get、set方法
}
@Entity
@Table(name = "sys_permission")
public class SysPermission {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "permission_name")
    private String permissionName;
    
    @Column(name = "permission_description")
    private String permissionDescription;
    
    @ManyToMany
    @JoinTable(name = "sys_role_permission", 
joinColumns = { @JoinColumn(name = "permission_id") }, inverseJoinColumns = {
            @JoinColumn(name = "role_id") })
    private Set<SysRole> roles;// 一個權限對應多個角色

//省略get、set的方法

}

4.配置realm實例,實際的認證和受權都是由Realm實例來完成的,我使用了一個本身寫的session工具類,登錄成功以後,把當前登錄的用戶信息放到session中。能夠本身去查看一下這個工具類,在這裏就不貼代碼了。git

public class MyRealm extends AuthorizingRealm implements Serializable {
	
    private static final long serialVersionUID = 1L;
    
    @Autowired
    private ISysUserDao userDao;
      
     /**
      * 權限認證,爲當前登陸的Subject授予角色和權限
      */
     @Override  
     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    	//獲取當前登陸輸入的用戶名
    	String loginName = (String)super.getAvailablePrincipal(principalCollection);
    	//到數據庫查是否有此對象
    	SysUser user = userDao.findByUsername(loginName);
    	if(user != null) {
    		//權限信息對象info,用來存放查出的用戶的全部的角色(role)及權限(permission)
    		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    		//用戶的角色集合
    		info.setRoles(user.getRoleName());
    		//用戶的角色對應的全部權限,若是隻使用角色定義訪問權限
    		Set<SysRole> roles = user.getRoles();
    		Iterator<SysRole> it = roles.iterator();
    		System.out.print("用戶具備的角色:");
    		while(it.hasNext()){
    			SysRole role = it.next();
    			info.addStringPermissions(role.getPermissionsName());
    			System.out.print(role.getRoleName() + " ");
    		}
    		System.out.println();
    		return info;
    	}
        return null;  
     }
     
     /**
      * 登錄認證  
      */
     @Override  
     protected AuthenticationInfo doGetAuthenticationInfo(  
             AuthenticationToken authenticationToken) throws AuthenticationException {  
    	 
    	//UsernamePasswordToken對象用來存放提交的登陸信息
    	UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    	//查出是否有此用戶
    	SysUser user = userDao.findByUsername(token.getUsername());
    	if(user != null) {
    		//登錄成功,把用戶的信息放到session中
    		HttpSessionUtil.put(SysConstant.LOGIN_USER, user);
    		return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName());
    	}
    	return null;
     }
      
}

5.編寫shiro的配置文件(spring-shiro.xml),讓spring去管理shiroweb

<?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: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/context 
http://www.springframework.org/schema/context/spring-context-4.0.xsd">  
      
        <!-- 1.配置要掃描的包 -->  
        <context:component-scan base-package="com.wzxy.nc"></context:component-scan>  
          
        <!--2.配置CacheManager實例:管理Shiro相關緩存操做  -->  
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">   
            <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>  
        </bean>  
          
        <!--3.配置realm實例,實際的認證和受權都是由Realm實例來完成的!  -->  
        <bean id="myRealm" class="com.wzxy.nc.realm.MyRealm"></bean>
      
        <!-- 4.配置 SecurityManager 實例. SecurityManager 是 Shiro 最核心的組件 -->  
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
            <property name="cacheManager" ref="cacheManager"/>  
            <property name="realm" ref="myRealm"/>  
        </bean>  
          
        <!--5.配置bean的後置處理器來自動調用Shiro中的bean的init和destroy方法。  -->  
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>  
          
          
        <!--6.配置使shiro註解起做用的bean,須要放在 lifecycleBeanPostProcessor後面 -->  
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"></bean>      
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
            <property name="securityManager" ref="securityManager"></property>  
        </bean>  
          
        <!--  
            7.配置哪些頁面須要被攔截,以及訪問這些頁面所需的權限 。  
                                   該bean中的id 屬性值必須和 web.xml 文件中配置的 filter 的 filter-name 值一致  
        -->  
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
            <property name="securityManager" ref="securityManager"></property>  
          
            <!--①配置登錄頁面  -->  
            <property name="loginUrl" value="/login.jsp"></property>  
            <property name="successUrl" value="/WEB-INF/pages/common/main.jsp"></property>  
            <property name="unauthorizedUrl" value="/unauthorize.jsp"></property>  
            <!--②配置須要被攔截的資源 以及訪問權限 -->  
            <property name="filterChainDefinitions">  
                <value>  
                    <!-- anon: 表示匿名的, 即任何人均可以訪問 -->
			        /js/** = anon
			        /css/**  = anon
			        /layer/**  = anon
			        /layui/**  = anon
			        /Uimaker/**  = anon
			        /User_login* = anon
			        
                    /login.jsp=anon 
                    /User_logout=logout  
                      
                    <!--③設置訪問具體資源的權限  -->  
                    /admin.jsp=roles[admin]  
                    /user.jsp=roles[user]  
                      
                    <!-- authc 表示必須通過認證以後才能夠訪問的頁面 -->  
                    /**=authc  
                </value>  
            </property>  
        </bean>  
    </beans>

6.在web.xml配置shiro的過濾器spring

<!-- shiro 核心配置文件 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

7.在userController編寫登錄的邏輯,在這裏我使用的是struts2。拋出異常的話其實不用那麼詳細,否則可能會有用戶惡意攻擊。在這裏僅供學習使用。數據庫

/**
	 * shiro 登錄
	 * @return
	 */
	public String login(){
		SysUser sessionUser = HttpSessionUtil.getCurrentUser();
		// 說明用戶已經登錄過了
		if(sessionUser != null){
    		super.setForwardPage("/WEB-INF/pages/common/main.jsp");   		
    		return SUCCESS;
		}
		
		if(user == null){
			return "relogin";
		}
		
		String username = user.getUsername();
		UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
		//獲取當前的Subject
		Subject currentUser = SecurityUtils.getSubject();
        try {
            currentUser.login(token);  
        }catch(UnknownAccountException uae){  
            return toLoginPage("未知帳戶");
        }catch(IncorrectCredentialsException ice){  
			return toLoginPage("密碼不正確");
        }catch(LockedAccountException lae){  
            return toLoginPage("帳戶已鎖定");
        }catch(ExcessiveAttemptsException eae){   
            return toLoginPage("用戶名或密碼錯誤次數過多");
        }catch(AuthenticationException ae){  
            //經過處理Shiro的運行時AuthenticationException
//就能夠控制用戶登陸失敗或密碼錯誤時的情景   
            ae.printStackTrace();  
            return toLoginPage("用戶名或密碼不正確");
        }
        
        //驗證是否登陸成功  
        if(currentUser.isAuthenticated()){  
            logger.info("用戶[" + username + "]登陸認證經過
(這裏能夠進行一些認證經過後的一些系統參數初始化操做)");
    		super.setForwardPage("/WEB-INF/pages/common/main.jsp");   		
    		return SUCCESS;
        }else {
        	token.clear();
            return toLoginPage(LOGIN_ERROR);
        }	
	}

8.在前端的菜單欄配置一下權限標籤,測試一下不一樣的用戶登錄是否能夠看見不一樣的菜單欄。喜歡的能夠點個贊,或者star一下項目。apache

<shiro:hasRole name="admin">
    <dd>
      <div class="title">
        <span><img src="Uimaker/images/leftico01.png" /></span>基礎信息管理
      </div>
      <ul class="menuson">
      	<li><cite></cite><a href="College_list" target="rightFrame">學院信息</a><i></i></li>
        <li><cite></cite><a href="User_list" target="rightFrame">用戶信息</a><i></i></li>
        <li><cite></cite><a href="Role_list" target="rightFrame">角色信息</a><i></i></li>
        <!-- <li><cite></cite><a href="Permission_list" target="rightFrame">權限信息</a><i></i></li> -->
      </ul>
    </dd>
    </shiro:hasRole>