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>