maven引入jar包html
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.3</version> </dependency>
web.xml中配置springjava
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在spring配置文件中引入shiro配置文件web
<import resource="applicationContext-shiro.xml"/>
<!-- Spring ApplicationContext 配置文件的路徑,可以使用通配符,多個路徑用,號分隔 此參數用於後面的Spring Context Loader --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/applicationContext-shiro.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<!-- 這裏的filter-name 要和spring 的applicationContext-shiro.xml 裏的 org.apache.shiro.spring.web.ShiroFilterFactoryBean 的bean name 相同 --> <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>
1.shiro中定義的filtername bean是要被wen.xml引用的,因此這裏的filtername要和shiro中的filtername相同
2.shiro的filter要定義在Struts2的filter以前,不然action沒法建立算法
<!-- 安全管理 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- Single realm app. If you have multiple realms, use the 'realms' property instead. --> <property name="realm" ref="authRealm"/><!-- 引用自定義的realm --> <!-- 二級緩存 --> <property name="cacheManager" ref="shiroEhcacheManager"/> </bean> <!-- 自定義權限認證 --> <bean id="authRealm" class="com.fyh.www.shiro.AuthRealm"> <!-- <property name="userService" ref="userService"/> --> <!-- 自定義密碼加密算法 --> <property name="credentialsMatcher" ref="passwordMatcher"/> </bean> <!-- 設置密碼加密策略 md5hash --> <bean id="passwordMatcher" class="com.fyh.www.shiro.CustomCredentialsMatcher"/> <!-- 此bean要被web.xml引用,和web.xml中的filtername同名 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/index.jsp" /> <!-- 沒有認證返回地址 --> <property name="unauthorizedUrl" value="/index2.jsp" /> <!-- 沒有受權返回地址 --> <property name="filterChainDefinitions"> <value> <!-- **表明任意子目錄 --> /login/** = anon /user/** = authc,roles[admin] /test/** = authc,perms[測試用的lkkk] </value> </property> </bean> <!-- 用戶受權/認證信息Cache, 採用EhCache 緩存 --> <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/> </bean> <!-- 保證明現了Shiro內部lifecycle函數的bean執行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.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"/> </bean>
<ehcache updateCheck="false" name="shiroCache"> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> </ehcache>
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher { public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { UsernamePasswordToken usertoken = (UsernamePasswordToken) token; //注意token.getPassword()拿到的是一個char[],不能直接用toString(),它底層實現不是咱們想的直接字符串,只能強轉 Object tokenCredentials = Encrypt.md5(String.valueOf(usertoken.getPassword()),usertoken.getUsername()); Object accountCredentials = getCredentials(info); //將密碼加密與系統加密後的密碼校驗,內容一致就返回true,不一致就返回false return equals(tokenCredentials, accountCredentials); } }
util中定義shiro的MD5加密方法spring
public class Encrypt { /* * 散列算法通常用於生成數據的摘要信息,是一種不可逆的算法,通常適合存儲密碼之類的數據, * 常見的散列算法如MD五、SHA等。通常進行散列時最好提供一個salt(鹽),好比加密密碼「admin」, * 產生的散列值是「21232f297a57a5a743894a0e4a801fc3」, * 能夠到一些md5解密網站很容易的經過散列值獲得密碼「admin」, * 即若是直接對密碼進行散列相對來講破解更容易,此時咱們能夠加一些只有系統知道的干擾數據, * 如用戶名和ID(即鹽);這樣散列的對象是「密碼+用戶名+ID」,這樣生成的散列值相對來講更難破解。 */ //高強度加密算法,不可逆 public static String md5(String password, String salt){ return new Md5Hash(password,salt,2).toString(); } public static void main(String[] args) { System.out.println(new Md5Hash("123456","tony",2).toString()); } }
在認證、受權內部實現機制中都有提到,最終處理都將交給Realm進行處理。
由於在Shiro中,最終是經過Realm來獲取應用程序中的用戶、角色及權限信息的。
一般狀況下,在Realm中會直接從咱們的數據源中獲取Shiro須要的驗證信息。能夠說,Realm是專用於安全框架的DAO.apache
public class AuthRealm extends AuthorizingRealm{ private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } //受權 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("受權"); //獲取當前用戶 User user = (User)principals.fromRealm(getName()).iterator().next(); //獲得權限字符串 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); Set<Role> roles = user.getRoles(); List<String> list = new ArrayList(); for(Role role :roles){ Set<Module> modules = role.getModules(); for(Module m:modules){ if(m.getCtype()==0){ //說明是主菜單 list.add(m.getCpermission()); } } } info.addStringPermissions(list); return info; } //認證 登陸 protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { System.out.println("認證"); UsernamePasswordToken upToken = (UsernamePasswordToken)token; User user = userService.findUserByName(upToken.getUsername()); if(user==null){ return null; }else{ AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName()); return info; } } }
public class LoginAction extends BaseAction { private static final long serialVersionUID = 1L; private String username; private String password; private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public String login() throws Exception { /* * shiro登陸方式:根據用戶名獲取密碼,密碼爲null非法用戶;有密碼檢查是否用戶填寫的密碼 * 登陸成功後無需往httpsession中存放當前用戶,這樣就跟web容器綁定,關聯太緊密;它本身建立 * subject對象,實現本身的session。這個跟web容器脫離,實現鬆耦合。 */ //調用shiro判斷當前用戶是不是系統用戶 Subject subject = SecurityUtils.getSubject(); //獲得當前用戶 //shiro是將用戶錄入的登陸名和密碼(未加密)封裝到token對象中 UsernamePasswordToken token = new UsernamePasswordToken(userName,password); try{ subject.login(token); //自動調用AuthRealm.doGetAuthenticationInfo //寫seesion,保存當前user對象 User curUser = (User)subject.getPrincipal(); //從shiro中獲取當前用戶 System.out.println(curUser.getDept().getDeptName()); //讓懶加載變成當即加載 Set<Role> roles = curUser.getRoles(); for(Role role :roles){ Set<Module> moduless = role.getModules(); for(Module m :moduless) System.out.println(m.getName()); } session.put(SysConstant.CURRENT_USER_INFO, curUser); //Principal 當前用戶對象 }catch(Exception ex){ super.put("errorInfo","用戶名密碼錯誤,請從新填寫!"); ex.printStackTrace(); return "login"; } return SUCCESS; } public String logout(){ session.remove(SysConstant.CURRENT_USER_INFO); //刪除session return "logout"; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%> <shiro:hasPermission name="sysadmin"> <span id="topmenu" onclick="toModule('sysadmin');">系統管理</span> </shiro:hasPermission>