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>