spring-shiro.xml:shiro主配置文件html
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" default-lazy-init="true"> <description>Shiro安全配置</description> <!-- 項目自定义的Realm --> <bean id="userShiroRealm" class="com.ssm.shiro.UserShiroRealm" /> <!--安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--设置自定义Realm --> <property name="realm" ref="userShiroRealm" /> <!--将缓存管理器,交给安全管理器 --> <property name="cacheManager" ref="shiroEhcacheManager" /> </bean> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 安全管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 默认的登录访问url --> <property name="loginUrl" value="/manage/login" /> <!-- 没有权限跳转的url --> <property name="unauthorizedUrl" value="/unauth"/> <!-- 自定义filter配置 --> <property name="filters"> <map> <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 --> </map> </property> <property name="filterChainDefinitions"> <value> /manage/login = anon<!-- 不须要认证 能够理解为匿名用户或游客 --> /manage/loginPost = anon<!-- 不须要认证 能够理解为匿名用户或游客 --> /guest/** = anon /manage/** = authc </value> </property> </bean> <!-- 用户受权信息Cache, 采用EhCache --> <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:conf/shiro/ehcache-shiro.xml" /> </bean> <!-- 在方法中 注入 securityManager ,进行代理控制 --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" /> <property name="arguments" ref="securityManager" /> </bean> <!-- 保证明现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- AOP式方法级权限检查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <!-- 启用shrio受权注解拦截方式 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> </beans>
ehcache-shiro.xml:Shrio缺省提供了基于ehCache来缓存用户认证信息和受权信息的实现,该xml为指定ehCache的配置文件前端
<?xml version="1.0" encoding="UTF-8"?> <ehcache updateCheck="false" name="shiroCache"> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> </ehcache>
ShiroUser.java:自定义Authentication对象,使得Subject除了携带用户的登陆名外还能够携带更多信息java
package com.ssm.shiro; import lombok.Data; import lombok.Getter; import java.io.Serializable; /** * @description:自定义Authentication对象,使得Subject除了携带用户的登陆名外还能够携带更多信息 */ @Data public class ShiroUser implements Serializable { private static final long serialVersionUID = -1373760761780840081L; public String userId; public String userName; public ShiroUser( String userId, String userName) { this.userId = userId; this.userName = userName; } }
UserShiroRealm.java:经过UserShiroRealm 继承Shiro验证用户登陆的类为自定义的AuthorizingRealmweb
package com.ssm.shiro; import com.ssm.model.User; import com.ssm.service.UserService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; /** * @description:shiro权限认证 * 经过UserShiroRealm 继承Shiro验证用户登陆的类为自定义的AuthorizingRealm */ public class UserShiroRealm extends AuthorizingRealm { private static Logger LOGGER = LoggerFactory.getLogger(UserShiroRealm.class); @Autowired private UserService userService; /** * 统一用户登陆入口 * Shiro登陆认证(原理:用户提交 用户名和密码 --- shiro 封装令牌 ---- realm 经过用户名将密码查询返回 ---- * shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登录控制 ) * * 其中密码加密方式:md5 32为加密 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { LOGGER.info("Shiro开始登陆认证"); UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user = userService.findUserByUserId(token.getUsername()); // 帐号不存在 if (user == null) { return null; } ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUserName()); // 认证缓存信息 return new SimpleAuthenticationInfo(shiroUser, user.getPassword().toCharArray(), getName()); } /** * Shiro权限认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Set<String> urlSet = new HashSet<String>(); urlSet.add("/manage/user"); urlSet.add("/manage/user/user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(urlSet); return info; } /** * 清除权限的缓存,但该方法执行后,前端页面的刷新会略有延迟,这是缓存形成的 * * @author jiangCaiJun */ public void updateAuthz(Long roleId){ Set<String> urlSet = new HashSet<String>(); List<Map<String, String>> roleResourceList= new ArrayList<>(); if (roleResourceList.size() >0) { for (Map<String, String> map : roleResourceList) { if(map != null){ urlSet.add(map.get("user")); } } } //注意:此处模拟塞入资源权限对应的标签,实际项目中,应从对应的资源与权限关系表中获取 urlSet.add("/manage/user"); urlSet.add("/manage/user/user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(urlSet); } }
前台ajax请求,传递username和password过来,封装成token,进行登陆校验ajax
@RequestMapping(value = "/loginPost", method = RequestMethod.POST) @ResponseBody public Object loginPost(String username, String password) { if (StringUtils.isBlank(username)) { return renderError("用户名不能为空"); } if (StringUtils.isBlank(password)) { return renderError("密码不能为空"); } Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); } catch (UnknownAccountException e) { logger.error("帐号不存在:{}", e); return renderError("帐号不存在"); } catch (DisabledAccountException e) { logger.error("帐号未启用:{}", e); return renderError("帐号未启用"); } catch (IncorrectCredentialsException e) { logger.error("密码错误:{}", e); return renderError("密码错误"); } catch (ExcessiveAttemptsException e) { logger.error("登陆失败屡次,帐户锁定:{}", e); return renderError("登陆失败屡次,帐户锁定10分钟"); } catch (RuntimeException e) { logger.error("未知错误,请联系管理员:{}", e); return renderError("未知错误,请联系管理员"); } return renderSuccess(); }
登陆页面以下spring
输入正确或错误的用户名和密码,返回分别以下apache
/** * Shiro权限认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Set<String> urlSet = new HashSet<String>(); urlSet.add("/manage/user"); urlSet.add("/manage/user/user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(urlSet); return info; }
@Controller @RequestMapping("/manage/user") @RequiresPermissions(value = { "/manage/user" }) public class UserController { private static final Logger LOG = Logger.getLogger(UserController.class); @Autowired private UserService userService; /** * 用户页 * * @return */ @RequiresPermissions(value = { "/manage/user/user" }) @RequestMapping(value = "/user", method = RequestMethod.GET) public String userManager(Model model) { String id = "1"; User user = userService.getUser(id); model.addAttribute("user", user); LOG.info(user.toString()); return "user/showUser"; } }
利用<shiro:hasPermission name="/manage/user/user"></shiro:hasPermission">来显示或隐藏<strong> /manage/user/user</strong>,若没有显示,则证实还没有拥有该权限。缓存
<shiro:hasPermission name="/manage/user/user"> <span>此处权限控制</span> <a href="${staticPath }/manage/user/user"> ${staticPath }/manage/user/user </a> </shiro:hasPermission>
注意:要在页面上添加shiro标签安全
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
该页面的信息以下图:mybatis
参考连接:Apache Shiro 使用手册(三)Shiro 受权 - kdboy - ITeye技术网站
具体信息以下图: