springmvc+shiro+freemarker实现的安全及权限管理
本文讲述了基于springmvc+shiro实现安全管理,shiro+freemarker实现权限验证。
首先我们从web.xml开始:

- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5" xmlns
="http://java.sun.com/xml/ns/javaee"- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:resources/tag-context.xml
- classpath:resources/shiro-context.xml
- </param-value>
- </context-param>
- <!-- log4j -->
- <context-param>
- <param-name>log4jConfigLocation</param-name>
- <param-value>classpath:resources/log4j.properties</param-value>
- </context-param>
- <context-param>
- <param-name>log4jDelay</param-name>
- <param-value>10000</param-value>
- </context-param>
- <!-- Spring -->
- <listener>
- <listener-class>
- org.springframework.web.context.ContextLoaderListener
- </listener-class>
- </listener>
- <listener>
- <listener-class>
- com.itrip.rp.listener.InitConfigListener
- </listener-class>
- </listener>
- <!-- log4j -->
- <listener>
- <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
- </listener>
- <!-- 日志记录过滤器 -->
- <filter>
- <filter-name>requestLogFilter</filter-name>
- <filter-class>
- com.itrip.rp.filter.RequestLogFilter
- </filter-class>
- </filter>
- <filter-mapping>
- <filter-name>requestLogFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- 编码过滤 -->
- <filter>
- <filter-name>encoding</filter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>UTF-8</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>encoding</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- shiro 安全过滤器 -->
- <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
- <filter>
- <filter-name>shiroFilter</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- <async-supported>true</async-supported>
- <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>
- <!-- SpringMVC -->
- <servlet>
- <servlet-name>resourcePlatform</servlet-name>
- <servlet-class>
- org.springframework.web.servlet.DispatcherServlet
- </servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:resources/applicationContext-*.xml
- </param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>resourcePlatform</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
- </web-app>

按照web.xml初始化顺序依次为:context-param--->listener--->filter--->servlet
tag-context.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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
- default-lazy-init="true">
- <!--后台权限标签-->
- <bean id="perm" class="com.itrip.rp.core.permission.PermissionDirective"/>
- </beans>

权限验证标签实现类是基于freemarker标签的实现方式,具体请看源代码:

- package com.itrip.rp.core.permission;
- import java.io.IOException;
- import java.util.Map;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.subject.Subject;
- import com.itrip.rp.common.Constants;
- import com.itrip.rp.core.freemarker.DirectiveUtils;
- import freemarker.core.Environment;
- import freemarker.template.TemplateDirectiveBody;
- import freemarker.template.TemplateDirectiveModel;
- import freemarker.template.TemplateException;
- import freemarker.template.TemplateModel;
- /**
- * 后台管理员权限许可
- *
- * @author Benny
- */
- public class PermissionDirective implements TemplateDirectiveModel {
- /***
- * 权限验证
- */
- @SuppressWarnings("unchecked")
- public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
- String url = DirectiveUtils.getString(Constants.PARAM_URL, params);
- Subject subject = SecurityUtils.getSubject();
- boolean pass = subject.isPermitted(url);
- if (pass) {
- body.render(env.getOut());
- }
- }
- }

- Constants.PARAM_URL="url"; //对应的值就是取freemarker标签中的url
标签形式如:
- <@perm url="/product/add"></@perm>
- Subject subject = SecurityUtils.getSubject(); //这一步是基于shiro获取认证用户对象
- boolean pass = subject.isPermitted(url); //这一步就是进行权限验证,权限验证通过返回true,反之返回false
这里还是非常简单的。
接下来让我们看看shiro的具体配置吧,还是先看源代码:

- <?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:util="http://www.springframework.org/schema/util"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
- default-lazy-init="true">
- <!-- Shiro拦截器 -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <property name="securityManager" ref="securityManager" />
- <property name="loginUrl" value="/login" />
- <property name="successUrl" value="/index" />
- <property name="filters">
- <util:map>
- <entry key="authc" value-ref="authcFilter" />
- <entry key="user" value-ref="userFilter" />
- <entry key="logout" value-ref="logoutFilter" />
- </util:map>
- </property>
- <!--authc登陆认证 user用户认证检查 logout退出 filter-->
- <property name="filterChainDefinitions">
- <value>
- /css/** = anon
- /img/** = anon
- /js/** = anon
- /favicon.ico = anon
- /login = authc
- /logout = logout
- /** = user
- </value>
- </property>
- </bean>
- <!-- 认证filter -->
- <bean id="authcFilter" class="com.itrip.rp.core.security.AdminAuthenticationFilter">
- <property name="adminLogin" value="/login"/>
- <property name="adminIndex" value="/index"/>
- </bean>
- <!-- 用户检查filter -->
- <bean id="userFilter" class="com.itrip.rp.core.security.AdminUserFilter"/>
- <!-- 退出系统filter -->
- <bean id="logoutFilter" class="com.itrip.rp.core.security.AdminLogoutFilter">
- <property name="logoutUrl" value="/login"/>
- </bean>
- <!-- 安全管理器 -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="authorizingRealm" />
- <property name="sessionManager" ref="sessionManager"/>
- <property name="cacheManager" ref="shiroEhcacheManager"/>
- </bean>
- <!-- 自定义登陆验证 -->
- <bean id="authorizingRealm" class="com.itrip.rp.core.security.AdminAuthorizingRealm">
- <property name="credentialsMatcher">
- <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
- <!-- 密码加密方式 -->
- <property name="hashAlgorithmName" value="MD5"/>
- <!-- true means hex encoded, false means base64 encoded -->
- <property name="storedCredentialsHexEncoded" value="true"/>
- <!-- 迭代次数 -->
- <property name="hashIterations" value="1" />
- </bean>
- </property>
- </bean>
- <!-- 缓存管理 -->
- <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
- <property name="cacheManagerConfigFile">
- <value>classpath:resources/ehcache-shiro.xml</value>
- </property>
- </bean>
- <!-- 会话Cookie 180000-->
- <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
- <constructor-arg value="sid"/>
- <property name="httpOnly" value="true"/>
- <property name="maxAge" value="180000"/>
- </bean>
- <!-- 会话ID生成器 -->
- <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
- <!-- 会话DAO -->
- <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
- <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
- <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
- </bean>
- <!-- 会话管理器 -->
- <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
- <property name="globalSessionTimeout" value="1800000"/>
- <property name="deleteInvalidSessions" value="true"/>
- <property name="sessionValidationSchedulerEnabled" value="true"/>
- <property name="sessionDAO" ref="sessionDAO"/>
- <property name="sessionIdCookieEnabled" value="true"/>
- <property name="sessionIdCookie" ref="sessionIdCookie"/>
- </bean>
- <!-- Shiro生命周期处理器-->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
- </beans>

shiro缓存配置文件:ehcache-shiro.xml

- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache>
- <diskStore path="java.io.tmpdir/rp-shiro-ehcache"/>
- <defaultCache
- maxElementsInMemory="10000"
- eternal="false"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- overflowToDisk="true"
- diskSpoolBufferSizeMB="30"
- maxElementsOnDisk="10000000"
- diskPersistent="false"
- diskExpiryThreadIntervalSeconds="120"/>
- <cache name="shiro-activeSessionCache"
- maxElementsInMemory="10000"
- overflowToDisk="true"
- eternal="true"
- timeToLiveSeconds="0"
- timeToIdleSeconds="0"
- diskPersistent="true"
- diskExpiryThreadIntervalSeconds="600"/>
- <cache name="org.apache.shiro.realm.text.PropertiesRealm-0-accounts"
- maxElementsInMemory="1000"
- eternal="true"
- overflowToDisk="true"/>
- </ehcache>

- /css/** = anon
- /img/** = anon
- /js/** = anon
- /favicon.ico = anon
这里是对静态资源的处理,静态资源不做认证。
重要的是以下三个拦截器,分别实现了用户认证,用户检查及退出系统过程。

- <property name="filters">
- <util:map>
- <entry key="authc" value-ref="authcFilter" />
- <entry key="user" value-ref="userFilter" />
- <entry key="logout" value-ref="logoutFilter" />
- </util:map>
- </property>

先看看用户认证authcFilter吧:

- package com.itrip.rp.core.security;
- import java.util.Date;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
- import org.apache.shiro.web.util.WebUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.itrip.rp.common.Constants;
- import com.itrip.rp.entity.beans.UserBaseInfo;
- import com.itrip.rp.exception.AuthenticationException;
- import com.itrip.rp.exception.DisabledException;
- import com.itrip.rp.exception.UsernameNotFoundException;
- import com.itrip.rp.service.AuthenticationService;
- import com.itrip.rp.service.LogService;
- import com.itrip.rp.service.UserService;
- import com.itrip.rp.session.SessionProvider;
- import com.itrip.rp.utils.DateFormatUtils;
- import com.itrip.rp.utils.RequestUtils;
- import com.itrip.rp.utils.SpringContextUtil;
- /**
- * 自定义登陆认证filter
- *
- * @author Benny
- */
- public class AdminAuthenticationFilter extends FormAuthenticationFilter {
- private Logger logger = LoggerFactory.getLogger("security");
- /**
- * 执行登陆操作
- */
- @Override
- protected boolean executeLogin(ServletRequest request, ServletResponse response) {
- AuthenticationToken token = createToken(request, response);
- if (token == null) {
- String msg = "create AuthenticationToken error";
- throw new IllegalStateException(msg);
- }
- String username = (String) token.getPrincipal();
- UserBaseInfo user = userService.login(username);
- if (user != null) {
- if (!user.getStatus()) {
- // 用户禁用
- return onLoginFailure(username, token, new DisabledException(), request, response);
- }
- } else {
- // 用户名不存在
- return onLoginFailure(username, token, new UsernameNotFoundException(), request, response);
- }
- try {
- Subject subject = getSubject(request, response);
- subject.login(token);
- return onLoginSuccess(user, token, subject, request, response);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- return onLoginFailure(username, token, new AuthenticationException(), request, response);
- }
- }
- /**
- * 初始化service及登陆跳转
- */
- @Override
- public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
- if (userService == null) {
- userService = (UserService) SpringContextUtil.getBean(UserService.class);
- }
- if (logService == null) {
- logService = (LogService) SpringContextUtil.getBean(LogService.class);
- }
- if (authService == null) {
- authService = (AuthenticationService) SpringContextUtil.getBean(AuthenticationService.class);
- }
- if (session == null) {
- session = (SessionProvider) SpringContextUtil.getBean(SessionProvider.class);
- }
- boolean isAllowed = isAccessAllowed(request, response, mappedValue);
- // 登陆跳转
- if (isAllowed && isLoginRequest(request, response)) {
- try {
- issueSuccessRedirect(request, response);
- } catch (Exception e) {
- logger.error("", e);
- }
- return false;
- }
- return isAllowed || onAccessDenied(request, response, mappedValue);
- }
- @Override
- protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse res = (HttpServletResponse) response;
- String successUrl = getAdminIndex() != null ? getAdminIndex() : super.getSuccessUrl();
- WebUtils.redirectToSavedRequest(req, res, successUrl);
- }
- @Override
- protected boolean isLoginRequest(ServletRequest req, ServletResponse resp) {
- String loginUrl = getAdminLogin() != null ? getAdminLogin() : super.getLoginUrl();
- return pathsMatch(loginUrl, req);
- }
- /**
- * 登陆成功
- */
- private boolean onLoginSuccess(UserBaseInfo user, AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response)
- throws Exception {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse res = (HttpServletResponse) response;
- // 记录用户登陆信息
- authService.login(user, RequestUtils.getIpAddr(req), req, res, session);
- // 将系统当前登陆用户信息放入session
- session.setAttribute(req, Constants.USERNAME, user.getNickName());
- Date lastLogin = authService.findSecond(user.getUserId());
- if (lastLogin != null) {
- session.setAttribute(req, Constants.LAST_LOGIN_TIME, DateFormatUtils.format(lastLogin, "yyyy-MM-dd HH:mm:ss"));
- }
- logService.loginSuccess(req, user.getUserId(), "login.log.loginSuccess");
- return super.onLoginSuccess(token, subject, request, response);
- }
- /**
- * 登陆失败
- */
- private boolean onLoginFailure(String username, AuthenticationToken token, AuthenticationException e, ServletRequest request,
- ServletResponse response) {
- HttpServletRequest req = (HttpServletRequest) request;
- logService.loginFailure(req, "login.log.loginFailure", "userName=" + username);
- request.setAttribute(Constants.MESSAGE, e.getMessage());
- return super.onLoginFailure(token, e, request, response);
- }
- private UserService userService;
- private LogService logService;
- private SessionProvider session;
- private AuthenticationService authService;
- private String adminIndex;
- private String adminLogin;
- public String getAdminIndex() {
- return adminIndex;
- }
- public void setAdminIndex(String adminIndex) {
- this.adminIndex = adminIndex;
- }
- public String getAdminLogin() {
- return adminLogin;
- }
- public void setAdminLogin(String adminLogin) {
- this.adminLogin = adminLogin;
- }
- }

需要说明的就是service的获取,在filter中获取spring自动注入bean需要通过spring上下文来获取,这里定义实现了获取spring上下文及注入bean的工具类SpringContextUtil.java稍后会详细说明这个类以及配置。
用户登录操作“/login”交由authcFilter处理,登录controller代码很简单:

- package com.itrip.rp.controller;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.lang.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.ModelMap;
- import org.springframework.web.bind.annotation.RequestMapping;
- import com.itrip.rp.common.Constants;
- import com.itrip.rp.controller.base.BaseController;
- /**
- * 系统登陆Controller
- *
- * @author Benny
- *
- */
- @Controller
- public class LoginController extends BaseController {
- protected static final Logger LOG = LoggerFactory.getLogger("run");
- /**
- * 登陆
- *
- * @param request
- * @param response
- * @param model
- * @return
- */
- @RequestMapping(value = "/login")
- public String login(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
- return "login";
- }
- /**
- * 系统首页
- *
- * @param message
- * @param request
- * @param response
- * @param model
- * @return
- */
- @RequestMapping(value = "/index")
- public String index(String message, HttpServletRequest request, HttpServletResponse response, ModelMap model) {
- if (!StringUtils.isBlank(message)) {
- model.addAttribute(Constants.MESSAGE, message);
- }
- return "product/index";
- }
- }

登录页面代码如下,username、password不要写错了:

- <!doctype html>
- <body>
- <form name="jvForm" action="${accessRoot}/login" method="post">
- <div class="loginbox round15">
- <dl>
- <dd class="fz24 pt30">User login</dd>
- <dd>
- <input type="text" placeholder="Login name" autocomplete="off" name="username">
- </dd>
- <dd>
- <input type="password" placeholder="Password" autocomplete="off" name="password" title="click enter login">
- </dd>
- <dd>
- <a href="javascript:document.jvForm.submit();" class="login-bt round3" id="btnLogin">Login</a>
- </dd>
- <font color="red" id="errTip">${message!}</font>
- </dl>
- </div>
- </form>
- </body>
- </html>

用户认证检查userFilter:

- package com.itrip.rp.core.security;
- import java.io.IOException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.shiro.web.filter.authc.UserFilter;
- import org.apache.shiro.web.util.WebUtils;
- /**
- * 用户认证检查filter
- *
- * @author Benny
- */
- public class AdminUserFilter extends UserFilter {
- // 未登陆重定向到登陆页
- protected void redirectToLogin(ServletRequest req, ServletResponse resp) throws IOException {
- HttpServletRequest request = (HttpServletRequest) req;
- HttpServletResponse response = (HttpServletResponse) resp;
- WebUtils.issueRedirect(request, response, getLoginUrl());
- }
- }

最后是退出系统logoutFilter:

- package com.itrip.rp.core.security;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import org.apache.commons.lang.StringUtils;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.web.filter.authc.LogoutFilter;
- import com.itrip.rp.common.Constants;
- /**
- * 退出系统 filter
- *
- * @author Benny
- */
- public class AdminLogoutFilter extends LogoutFilter {
- @Override
- protected String getRedirectUrl(ServletRequest req, ServletResponse resp, Subject subject) {
- HttpServletRequest request = (HttpServletRequest) req;
- String redirectUrl = request.getParameter(Constants.RETURN_URL);
- if (StringUtils.isBlank(redirectUrl)) {
- redirectUrl = getLogoutUrl();
- if (StringUtils.isBlank(redirectUrl)) {
- redirectUrl = getRedirectUrl();
- }
- }
- return redirectUrl;
- }
- private String logoutUrl;
- public void setLogoutUrl(String logoutUrl) {
- this.logoutUrl = logoutUrl;
- }
- public String getLogoutUrl() {
- return logoutUrl;
- }
- }

接下来让我们看看自定义实现的登录认证及授权Realm吧:

- package com.itrip.rp.core.security;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
- 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.UsernamePasswordToken;
- 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.apache.shiro.subject.SimplePrincipalCollection;
- import org.apache.shiro.util.CollectionUtils;
- import com.itrip.rp.entity.beans.UserBaseInfo;
- import com.itrip.rp.service.UserService;
- import com.itrip.rp.utils.SpringContextUtil;
- /**
- * 认证及授权Realm
- *
- * @author Benny
- */
- public class AdminAuthorizingRealm extends AuthorizingRealm {
- /**
- * 登陆认证
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
- UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
- if (userService == null) {
- userService = (UserService) SpringContextUtil.getBean(UserService.class);
- }
- UserBaseInfo user = userService.login(token.getUsername());
- if (user != null) {
- return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
- } else {
- return null;
- }
- }
- /**
- * 授权
- */
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- // TODO Auto-generated method stub
- UserBaseInfo user = (UserBaseInfo) principals.getPrimaryPrincipal();
- SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
- if (user != null) {
- if (userService == null) {
- userService = (UserService) SpringContextUtil.getBean(UserService.class);
- }
- List<String> perms = userService.getPerms(user.getUserId());
- Set<String> set = new HashSet<String>(perms);
- if (!CollectionUtils.isEmpty(perms)) {
- // 权限加入AuthorizationInfo认证对象
- auth.setStringPermissions(set);
- }
- }
- return auth;
- }
- /**
- * 清空用户权限缓存
- *
- * @param username
- */
- public void removeUserAuthorizationInfoCache(String username) {
- SimplePrincipalCollection pc = new SimplePrincipalCollection();
- pc.add(username, super.getName());
- super.clearCachedAuthorizationInfo(pc);
- }
- private UserService userService;
- }

登录认证成功后将用户对象放入AuthenticationInfo中,以便授权过程中直接使用用户对象。
授权操作只有在第一次进行权限验证的时候才会初始化(比较重要),将用户所拥有的所有权限放入AuthorizationInfo对象。
当用户权限发生变化,就需要手动调用removeUserAuthorizationInfoCache方法去清除用户权限缓存。
最后再看看springmvc配置文件:

- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:jdbc="http://www.springframework.org/schema/jdbc"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
- http://www.springframework.org/schema/jdbc
- http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
- <!-- 自动依赖注入 -->
- <context:component-scan base-package="com.itrip.rp" />
- <!-- 文件上传解析器 id 必须为multipartResolver -->
- <bean id="multipartResolver"
- class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <property name="maxUploadSize" value="10485760" />
- </bean>
- <!-- 没有自定义实现拦截器的时候必须声明spring默认配置 -->
- <!-- <mvc:annotation-driven/> -->
- <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
- <property name="interceptors">
- <list>
- <ref bean="adminContextInterceptor"/>
- </list>
- </property>
- </bean>
- <bean id="adminContextInterceptor" class="com.itrip.rp.interceptor.AdminContextInterceptor">
- <property name="excludeUrls">
- <list>
- <value>/login</value>
- <value>/logout</value>
- </list>
- </property>
- </bean>
- <!-- 静态资源 -->
- <mvc:resources location="/img/" mapping="/img/**" />
- <mvc:resources location="/js/" mapping="/js/**" />
- <mvc:resources location="/css/" mapping="/css/**" />
- <!-- @responsebody标签返回对象格式配置 -->
- <bean
- class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
- <!-- 配置信息转换,将用@responsebody注解的返回值转换为json返回前台,编码为utf-8 -->
- <property name="messageConverters">
- <list>
- <bean
- class="org.springframework.http.converter.StringHttpMessageConverter">
- <property name="supportedMediaTypes">
- <list>
- <value>text/html;charset=UTF-8</value
>- </list>
- </property>
- </bean>
- <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
- <property name="supportedMediaTypes">
- <list>
- <value>application/json;charset=UTF-8</value>
- </list>
- </property>
- </bean>
- </list>
- </property>
- </bean>
- <!-- 异常处理 -->
- <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
- <property name="exceptionMappings">
- <props>
- <prop key="java.lang.Exception">error/404</prop>
- <prop key="java.lang.Throwable">error/404</prop>
- </props>
- </property>
- <property name="warnLogCategory" value="WARN" />
- <property name="defaultErrorView" value="error/404" />
- </bean>
- <!-- 默认视图配置welcome页 -->
- <mvc:view-controller path="/" view-name="product/index"/>
- <!-- freemarker视图解析器配置 -->
- <bean id="freemarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
- <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
- <!-- 视图名后缀 -->
- <property name="suffix" value=".html" />
- <property name="contentType" value="text/html; charset=UTF-8" />
- <!-- request/session==true请求和会话属性都被复制到模板的属性集中,此时spring必须设置为true -->
- <property name="exposeRequestAttributes" value="false" />
- <property name="exposeSessionAttributes" value="false" />
- <property name="exposeSpringMacroHelpers" value="true" />
- </bean>
- <bean id="freemarkerConfig"
- class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
- <!-- 模板路径 -->
- <property name="templateLoaderPath" value="/view/" />
- <property name="freemarkerVariables">
- <map>
- <!--后台管理权限控制 -->
- <entry key="perm" value-ref="perm" />
- </map>
- </property>
- <property name="freemarkerSettings">
- <props>
- <prop key="template_update_delay">0</prop>
- <prop key="defaultEncoding">UTF-8</prop>
- <prop key="url_escaping_charset">UTF-8</prop>
- <prop key="locale">zh_CN</prop>
- <prop key="boolean_format">true,false</prop>
- <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
- <prop key="date_format">yyyy-MM-dd</prop>
- <prop key="time_format">HH:mm:ss</prop>
- <prop key="number_format">0.######</prop>
- <prop key="whitespace_stripping">true</prop>
- </props>
- </property>
- </bean>
- <!-- session持有者 -->
- <bean id="sessionProvider" class="com.itrip.rp.session.HttpSessionProvider" />
- <!-- spring上下文工具类 -->
- <bean id="springContextUtil " class="com.itrip.rp.utils.SpringContextUtil" />
- <!-- 数据库配置 -->
- <import resource="classpath:resources/database-context.xml"/>
- </beans>

这里要重点说明的就是之前提到过的spring上下文工具类:SpringContextUtil.java

- package com.itrip.rp.utils;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.NoSuchBeanDefinitionException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- /**
- * spring上下文工具类
- *
- * @author Benny
- *
- */
- public class SpringContextUtil implements ApplicationContextAware {
- private static ApplicationContext applicationContext; // Spring应用上下文环境
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- SpringContextUtil.applicationContext = applicationContext;
- }
- public static ApplicationContext getApplicationContext() {
- return applicationContext;
- }
- public static Object getBean(String name) throws BeansException {
- return applicationContext.getBean(name);
- }
- public static Object getBean(Class<?> requiredType) throws BeansException {
- return applicationContext.getBean(requiredType);
- }
- public static Object getBean(String name, Class<?> requiredType) throws BeansException {
- return applicationContext.getBean(name, requiredType);
- }
- public static boolean containsBean(String name) {
- return applicationContext.containsBean(name);
- }
- public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
- return applicationContext.isSingleton(name);
- }
- public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
- return applicationContext.getType(name);
- }
- public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
- return applicationContext.getAliases(name);
- }
- }

至此,springmvc+shiro安全管理+freemarker标签权限验证就完成了。
基于URL权限验证的方式也非常简单,只需要在自定义拦截器中对所有url进行权限校验即可,同样也是使用shiro权限校验机制,跟freemarker标签式的权限校验一致,看看拦截器源代码:

- package com.itrip.rp.interceptor;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.subject.Subject;
- import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
- import org.springframework.web.util.UrlPathHelper;
- /**
- * URI拦截器 用户权限验证
- *
- * @author Benny
- */
- public class AdminContextInterceptor extends HandlerInterceptorAdapter {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- // 获取请求链接
- String uri = getURI(request);
- // 排除例外URI,例如:登陆、退出
- if (exclude(uri)) {
- return true;
- }
- Subject subject = SecurityUtils.getSubject();
- boolean pass = subject.isPermitted(uri);
- if (pass) {
- return true;
- } else {
- // 跳转至异常处理
- throw new Exception();
- }
- }
- /**
- * 判断是否例外uri
- *
- * @param uri
- * @return
- */
- private boolean exclude(String uri) {
- if (excludeUrls != null) {
- for (String exc : excludeUrls) {
- // 允许以excludeurl结尾的请求
- if (uri.endsWith(exc)) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * 获取请求URL
- *
- * @param request
- * @author Benny
- * @return
- */
- private static String getURI(HttpServletRequest request) {
- UrlPathHelper helper = new UrlPathHelper();
- return helper.getOriginatingRequestUri(request);
- }
- private String[] excludeUrls;
- public void setExcludeUrls(String[] excludeUrls) {
- this.excludeUrls = excludeUrls;
- }
- }

以上实现了shiro安全管理+freemarker标签式的权限控制+系统全局url权限控制,基本满足大部分web项目的权限管理。
到此结束!
使用的jar包以及版本在此说明一下:
shiro相关jar包:

- <!-- shiro配置start -->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-web</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-ehcache</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-quartz</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.2.2</version>
- </dependency>
- <!-- shiro配置end -->

spring使用版本为3.0.5
</div>
springmvc+shiro+freemarker实现的安全及权限管理的更多相关文章
- 基于shiro+jwt的真正rest url权限管理,前后端分离
代码地址如下:http://www.demodashi.com/demo/13277.html bootshiro & usthe bootshiro是基于springboot+shiro+j ...
- 【shiro】(2)---基于RUL的权限管理
基于RUL的权限管理 我想在写shiro权限管理认证前,先来一个基于URL实现的权限管理控制. 一.基于URI的权限业务逻辑 实现思路: 将系统操作的每个url配置在权限表中,将权限对应 ...
- Shiro集成SSM基于动态URL权限管理(二)
这个案例基于上一个demo扩展而来.所以数据库表,在Shiro集成SSM基于URL权限管理(一)开篇的一致.如果上个demo操作的建议重新导入一次,避免出现问题. 而这次都不是通过固定写在方法上的注解 ...
- SpringMVC拦截器2(资源和权限管理)(作为补充说明)
SpringMVC拦截器(资源和权限管理) 1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServle ...
- 《shiro》视频目录---1、权限管理-shiro
\day01_shiro\0323\10realm支持散列.avi;\day01_shiro\0323\1权限管理原理.avi;\day01_shiro\0323\2权限管理解决方案.avi;\day ...
- Springmvc+Shiro实战
原文链接:http://blog.csdn.net/qq_37936542/article/details/79010449 springmvc+shiro实现系统粗细粒度的权限管理步骤: 1:表格设 ...
- 7. 整合shiro,搭建粗粒度权限管理
shiro是一个易用的权限管理框架,只需提供一个Realm即可在项目中使用,本文就将结合上一篇中搭建的权限模块.角色模块和用户模块来搭建一个粗粒度的权限管理系统,具体如下:1. 添加shiro依赖和与 ...
- springboot学习笔记:11.springboot+shiro+mysql+mybatis(通用mapper)+freemarker+ztree+layui实现通用的java后台管理系统(权限管理+用户管理+菜单管理)
一.前言 经过前10篇文章,我们已经可以快速搭建一个springboot的web项目: 今天,我们在上一节基础上继续集成shiro框架,实现一个可以通用的后台管理系统:包括用户管理,角色管理,菜单管理 ...
- SpringMVC+Shiro权限管理【转】
1.权限的简单描述 2.实例表结构及内容及POJO 3.Shiro-pom.xml 4.Shiro-web.xml 5.Shiro-MyShiro-权限认证,登录认证层 6.Shiro-applica ...
随机推荐
- 线段树 hdu3642 Get The Treasury
不得不说,这是一题很经典的体积并.. 然而还是debug了2个多小时... 首先思路:按z的大小排序. 然后相当于扫描面一样,,从体积的最下方向上方扫描,遇到这个面 就将相应的两条线增加到set中,或 ...
- 【Android界面实现】使用GestureOverlayView控件实现手势识别
在Android开发中,我们不光能够使用已有的实现方式.并且,我们还能够利用Android这个智能手机平台.实现一些比較有特色的功能. 本篇文章介绍使用GestureOverlayView这个控件.实 ...
- C++对象模型——Inline Functions(第四章)
4.5 Inline Functions 以下是Point class 的一个加法运算符的可能实现内容: class Point { friend Point operator+(const Poin ...
- Python画图工具matplotlib的安装
今天在机子上安装matplotlib遇到一些问题,特将此记录下来,供大家分享以少走弯路. 1:下载matplotlib 去官网上下载你所须要的版本号http://matplotlib.org/down ...
- jquery05 继承
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- Onvif开发之客户端鉴权获取参数篇
前面一篇已经介绍了客户端如何发些设备,下面这篇简单介绍下在发现设备后,如何通过ONVIF协议来获取设备的相关参数 ONVIF中不管是客户端还是设备端,最先实现的接口都是关于能力的那个接口,在客户端实现 ...
- Css学习总结(1)——20个很有用的CSS技巧
1. 黑白图像 这段代码会让你的彩色照片显示为黑白照片,是不是很酷? img.desaturate { filter: grayscale(100%); -webkit-filter: graysca ...
- 将已有的Eclipse项目转化为Maven项目
将已有的Eclipse项目转化为Maven项目 我们之前在Eclipse IDE完成的Java命令行项目.Java Web项目也使用了构建工具--Ant,它帮助我们编译.运行Java源代码(无需我们自 ...
- [LuoguP4892]GodFly的寻宝之旅 状压DP
链接 基础状压DP,预处理出sum,按照题意模拟即可 复杂度 \(O(n^22^n)\) #include<bits/stdc++.h> #define REP(i,a,b) for(in ...
- native.js是什么且如何使用
native.js是什么且如何使用 一.总结 一句话总结:Native.js技术,简称NJS,是一种将手机操作系统的原生对象转义,映射为JS对象,在JS里编写原生代码的技术.Native.js不是一个 ...