shiro+spring web项目
1.在web.xml中配置filter

<!-- Apache 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>
2.配置文件spring-shiro.xml

<?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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" default-lazy-init="true"> <description>Shiro Configuration</description> <!-- 加载配置属性文件 --> <context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" /> <!-- Shiro权限过滤过滤器定义 --> <bean name="shiroFilterChainDefinitions" class="java.lang.String"> <constructor-arg> <value> /static/** = anon /userfiles/** = anon ${adminPath}/cas = cas ${adminPath}/login = authc ${adminPath}/logout = logout ${adminPath}/** = user /act/rest/service/editor/** = perms[act:model:edit] /act/rest/service/model/** = perms[act:model:edit] /act/rest/service/** = user /ReportServer/** = user </value> </constructor-arg> </bean> <!-- 安全认证过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /><!-- <property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> --> <property name="loginUrl" value="${adminPath}/login" /> <property name="successUrl" value="${adminPath}?login" /> <property name="filters"> <map> <entry key="cas" value-ref="casFilter"/> <entry key="authc" value-ref="formAuthenticationFilter"/> </map> </property> <property name="filterChainDefinitions"> <ref bean="shiroFilterChainDefinitions"/> </property> </bean> <!-- CAS认证过滤器 --> <bean id="casFilter" class="org.apache.shiro.cas.CasFilter"> <property name="failureUrl" value="${adminPath}/login"/> </bean> <!-- 定义Shiro安全管理配置 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="systemAuthorizingRealm" /> <property name="sessionManager" ref="sessionManager" /> <property name="cacheManager" ref="shiroCacheManager" /> </bean> <!-- 自定义会话管理配置 --> <bean id="sessionManager" class="com.thinkgem.jeesite.common.security.shiro.session.SessionManager"> <property name="sessionDAO" ref="sessionDAO"/> <!-- 会话超时时间,单位:毫秒 --> <property name="globalSessionTimeout" value="${session.sessionTimeout}"/> <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话 --> <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/> <!-- <property name="sessionValidationSchedulerEnabled" value="false"/> --> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionIdCookie" ref="sessionIdCookie"/> <property name="sessionIdCookieEnabled" value="true"/> </bean> <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID, 当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! --> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg name="name" value="jeesite.session.id"/> </bean> <!-- 自定义Session存储容器 --> <!-- <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.JedisSessionDAO"> --> <!-- <property name="sessionIdGenerator" ref="idGen" /> --> <!-- <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> --> <!-- </bean> --> <bean id="sessionDAO" class="com.thinkgem.jeesite.common.security.shiro.session.CacheSessionDAO"> <property name="sessionIdGenerator" ref="idGen" /> <property name="activeSessionsCacheName" value="activeSessionsCache" /> <property name="cacheManager" ref="shiroCacheManager" /> </bean> <!-- 定义授权缓存管理器 --> <!-- <bean id="shiroCacheManager" class="com.thinkgem.jeesite.common.security.shiro.cache.SessionCacheManager" /> --> <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="cacheManager"/> </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"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> </beans>
所有的登录成功啊,权限不够啊什么之类的,都在这个配置文件里
3.自定义realms

/** * Copyright © 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved. */ package com.thinkgem.jeesite.modules.sys.security; import java.io.Serializable; import java.util.Collection; import java.util.List; import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.Permission; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.thinkgem.jeesite.common.config.Global; import com.thinkgem.jeesite.common.servlet.ValidateCodeServlet; import com.thinkgem.jeesite.common.utils.Encodes; import com.thinkgem.jeesite.common.utils.SpringContextHolder; import com.thinkgem.jeesite.common.web.Servlets; import com.thinkgem.jeesite.modules.sys.entity.Menu; import com.thinkgem.jeesite.modules.sys.entity.Role; import com.thinkgem.jeesite.modules.sys.entity.User; import com.thinkgem.jeesite.modules.sys.service.SystemService; import com.thinkgem.jeesite.modules.sys.utils.LogUtils; import com.thinkgem.jeesite.modules.sys.utils.UserUtils; import com.thinkgem.jeesite.modules.sys.web.LoginController; /** * 系统安全认证实现类 * @author ThinkGem * @version 2014-7-5 */ @Service //@DependsOn({"userDao","roleDao","menuDao"}) public class SystemAuthorizingRealm extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger(getClass()); private SystemService systemService; /** * 认证回调函数, 登录时调用 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; int activeSessionSize = getSystemService().getSessionDao().getActiveSessions(false).size(); if (logger.isDebugEnabled()){ logger.debug("login submit, active session size: {}, username: {}", activeSessionSize, token.getUsername()); } // 校验登录验证码 if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){ Session session = UserUtils.getSession(); String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE); if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){ throw new AuthenticationException("msg:验证码错误, 请重试."); } } // 校验用户名密码 User user = getSystemService().getUserByLoginName(token.getUsername()); if (user != null) { if (Global.NO.equals(user.getLoginFlag())){ throw new AuthenticationException("msg:该已帐号禁止登录."); } byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16)); return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()), user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName()); } else { return null; } } /** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Principal principal = (Principal) getAvailablePrincipal(principals); // 获取当前已登录的用户 if (!Global.TRUE.equals(Global.getConfig("user.multiAccountLogin"))){ Collection<Session> sessions = getSystemService().getSessionDao().getActiveSessions(true, principal, UserUtils.getSession()); if (sessions.size() > 0){ // 如果是登录进来的,则踢出已在线用户 if (UserUtils.getSubject().isAuthenticated()){ for (Session session : sessions){ getSystemService().getSessionDao().delete(session); } } // 记住我进来的,并且当前用户已登录,则退出当前用户提示信息。 else{ UserUtils.getSubject().logout(); throw new AuthenticationException("msg:账号已在其它地方登录,请重新登录。"); } } } User user = getSystemService().getUserByLoginName(principal.getLoginName()); if (user != null) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); List<Menu> list = UserUtils.getMenuList(); for (Menu menu : list){ if (StringUtils.isNotBlank(menu.getPermission())){ // 添加基于Permission的权限信息 for (String permission : StringUtils.split(menu.getPermission(),",")){ info.addStringPermission(permission); } } } // 添加用户权限 info.addStringPermission("user"); // 添加用户角色信息 for (Role role : user.getRoleList()){ info.addRole(role.getEnname()); } // 更新登录IP和时间 getSystemService().updateUserLoginInfo(user); // 记录登录日志 LogUtils.saveLog(Servlets.getRequest(), "系统登录"); return info; } else { return null; } } @Override protected void checkPermission(Permission permission, AuthorizationInfo info) { authorizationValidate(permission); super.checkPermission(permission, info); } @Override protected boolean[] isPermitted(List<Permission> permissions, AuthorizationInfo info) { if (permissions != null && !permissions.isEmpty()) { for (Permission permission : permissions) { authorizationValidate(permission); } } return super.isPermitted(permissions, info); } @Override public boolean isPermitted(PrincipalCollection principals, Permission permission) { authorizationValidate(permission); return super.isPermitted(principals, permission); } @Override protected boolean isPermittedAll(Collection<Permission> permissions, AuthorizationInfo info) { if (permissions != null && !permissions.isEmpty()) { for (Permission permission : permissions) { authorizationValidate(permission); } } return super.isPermittedAll(permissions, info); } /** * 授权验证方法 * @param permission */ private void authorizationValidate(Permission permission){ // 模块授权预留接口 } /** * 设定密码校验的Hash算法与迭代次数 */ @PostConstruct public void initCredentialsMatcher() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SystemService.HASH_ALGORITHM); matcher.setHashIterations(SystemService.HASH_INTERATIONS); setCredentialsMatcher(matcher); } // /** // * 清空用户关联权限认证,待下次使用时重新加载 // */ // public void clearCachedAuthorizationInfo(Principal principal) { // SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName()); // clearCachedAuthorizationInfo(principals); // } /** * 清空所有关联认证 * @Deprecated 不需要清空,授权缓存保存到session中 */ @Deprecated public void clearAllCachedAuthorizationInfo() { // Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); // if (cache != null) { // for (Object key : cache.keys()) { // cache.remove(key); // } // } } /** * 获取系统业务对象 */ public SystemService getSystemService() { if (systemService == null){ systemService = SpringContextHolder.getBean(SystemService.class); } return systemService; } /** * 授权用户信息 */ public static class Principal implements Serializable { private static final long serialVersionUID = 1L; private String id; // 编号 private String loginName; // 登录名 private String name; // 姓名 private boolean mobileLogin; // 是否手机登录 // private Map<String, Object> cacheMap; public Principal(User user, boolean mobileLogin) { this.id = user.getId(); this.loginName = user.getLoginName(); this.name = user.getName(); this.mobileLogin = mobileLogin; } public String getId() { return id; } public String getLoginName() { return loginName; } public String getName() { return name; } public boolean isMobileLogin() { return mobileLogin; } // @JsonIgnore // public Map<String, Object> getCacheMap() { // if (cacheMap==null){ // cacheMap = new HashMap<String, Object>(); // } // return cacheMap; // } /** * 获取SESSIONID */ public String getSessionid() { try{ return (String) UserUtils.getSession().getId(); }catch (Exception e) { return ""; } } @Override public String toString() { return id; } } }
4.form表单过滤器(不改也可以)

/** * Copyright © 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved. */ package com.thinkgem.jeesite.modules.sys.security; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.web.util.WebUtils; import org.springframework.stereotype.Service; import com.thinkgem.jeesite.common.utils.StringUtils; /** * 表单验证(包含验证码)过滤类 * @author ThinkGem * @version 2014-5-19 */ @Service public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter { public static final String DEFAULT_CAPTCHA_PARAM = "validateCode"; public static final String DEFAULT_MOBILE_PARAM = "mobileLogin"; public static final String DEFAULT_MESSAGE_PARAM = "message"; private String captchaParam = DEFAULT_CAPTCHA_PARAM; private String mobileLoginParam = DEFAULT_MOBILE_PARAM; private String messageParam = DEFAULT_MESSAGE_PARAM; @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { String username = getUsername(request); String password = getPassword(request); if (password==null){ password = ""; } boolean rememberMe = isRememberMe(request); String host = StringUtils.getRemoteAddr((HttpServletRequest)request); String captcha = getCaptcha(request); boolean mobile = isMobileLogin(request); return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha, mobile); } public String getCaptchaParam() { return captchaParam; } protected String getCaptcha(ServletRequest request) { return WebUtils.getCleanParam(request, getCaptchaParam()); } public String getMobileLoginParam() { return mobileLoginParam; } protected boolean isMobileLogin(ServletRequest request) { return WebUtils.isTrue(request, getMobileLoginParam()); } public String getMessageParam() { return messageParam; } /** * 登录成功之后跳转URL */ public String getSuccessUrl() { return super.getSuccessUrl(); } @Override protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception { // Principal p = UserUtils.getPrincipal(); // if (p != null && !p.isMobileLogin()){ WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true); // }else{ // super.issueSuccessRedirect(request, response); // } } /** * 登录失败调用事件 */ @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { String className = e.getClass().getName(), message = ""; if (IncorrectCredentialsException.class.getName().equals(className) || UnknownAccountException.class.getName().equals(className)){ message = "用户或密码错误, 请重试."; } else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")){ message = StringUtils.replace(e.getMessage(), "msg:", ""); } else{ message = "系统出现点问题,请稍后再试!"; e.printStackTrace(); // 输出到控制台 } request.setAttribute(getFailureKeyAttribute(), className); request.setAttribute(getMessageParam(), message); return true; } }
当用户登录的时候,会到过滤器里,它有什么作用呢,最主要的作用是:调用createToken()方法返回token,其中token中包含着用户登录所需要的信息,然后到realms认证方法的时候,就可以取出token,然后查库进行操作。
不加这个过滤器,他会默认用org.apache.shiro.web.filter.authc.FormAuthenticationFilter,如果有个性需求(比如登录的时候要加一个短信验证码啊什么之类的),你就需要自己实现表单过滤器。本身的org.apache.shiro.web.filter.authc.FormAuthenticationFilter,只有用户名和密码之类常用的。
5.Controller类,shiro权限
@RequiresPermissions("sys:menu:view")
@RequestMapping("list"})
public String list(Model model) {
return "/list";
}
url对应的权限配置
6.jsp页面
Jsp页面添加:
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>
标签名称 |
标签条件(均是显示标签内容) |
<shiro:authenticated> |
登录之后 |
<shiro:notAuthenticated> |
不在登录状态时 |
<shiro:guest> |
用户在没有RememberMe时 |
<shiro:user> |
用户在RememberMe时 |
<shiro:hasAnyRoles name="abc,123" > |
在有abc或者123角色时 |
<shiro:hasRole name="abc"> |
拥有角色abc |
<shiro:lacksRole name="abc"> |
没有角色abc |
<shiro:hasPermission name="abc"> |
拥有权限资源abc |
<shiro:lacksPermission name="abc"> |
没有abc权限资源 |
<shiro:principal> |
显示用户身份名称 |
<shiro:principal property="username"/> 显示用户身份中的属性值
7.配置文件解释:
1.自定义过滤器

<!-- Shiro权限过滤过滤器定义 --> <bean name="shiroFilterChainDefinitions" class="java.lang.String"> <constructor-arg> <value> /static/** = anon /userfiles/** = anon ${adminPath}/cas = cas ${adminPath}/login = authc ${adminPath}/logout = logout ${adminPath}/** = user /act/rest/service/editor/** = perms[act:model:edit] /act/rest/service/model/** = perms[act:model:edit] /act/rest/service/** = user /ReportServer/** = user </value> </constructor-arg> </bean>

过滤器简称 对应的java类 anon org.apache.shiro.web.filter.authc.AnonymousFilter authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port org.apache.shiro.web.filter.authz.PortFilter rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl org.apache.shiro.web.filter.authz.SslFilter user org.apache.shiro.web.filter.authc.UserFilter logout org.apache.shiro.web.filter.authc.LogoutFilter anon:例子/admins/**=anon 没有参数,表示可以匿名使用。 authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数 perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查 1、在applicationContext-shiro.xml中配置filter规则 <!--商品查询需要商品查询权限 --> /items/queryItems.action = perms[item:query] 2、用户在认证通过后,请求/items/queryItems.action 3、被PermissionsAuthorizationFilter拦截,发现需要“item:query”权限 4、PermissionsAuthorizationFilter调用realm中的doGetAuthorizationInfo获取数据库中正确的权限 5、PermissionsAuthorizationFilter对item:query 和从realm中获取权限进行对比,如果“item:query”在realm返回的权限列表中,授权通过。
2.shiro安全过滤器

<!-- 安全认证过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /><!-- <property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> --> <property name="loginUrl" value="${adminPath}/login" /> <property name="successUrl" value="${adminPath}?login" /> <property name="filters"> <map> <entry key="cas" value-ref="casFilter"/> <entry key="authc" value-ref="formAuthenticationFilter"/> </map> </property> <property name="filterChainDefinitions"> <ref bean="shiroFilterChainDefinitions"/> </property> </bean>
loginUrl:登录地址
successUrl:成功登录地址
filterChainDefinitions:自定义的过滤器添加到这儿,等等的参数
流程:
1.通过浏览器访问路径,配置文件查看,是否需要认证等,如果不需要,直接访问controller
2.如果需要认证,通过配置文件的loginUrl,跳到这个地址,输入用户名、密码等
3.登录:1.访问自定义的form表单过滤器FormAuthenticationFilter(自己起的名字和shiro一样了,所以。。)的createToken方法,装配token;
如果没有自定义表单过滤器,默认的FormAuthenticationFilter会自动装配表单token
2.访问自定义realms的认证方法doGetAuthenticationInfo(),查库(或者缓存),判断用户名和密码是否正确。
4.如果登录之后访问的url,通过配置文件里的配置需要权限:
调用自定义realms的授权方法:doGetAuthorizationInfo(),查库(或者缓存),查出用户权限,判断是否拥有权限,没权访问,跳到响应的refuse配置的路径,有权访问,跳到响应的url