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>
最新文章
- JS倒计时执行操作
- 《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型
- TouchSlide1.1,手机上的幻灯片
- cf727e
- git入门学习(一):github for windows上传本地项目到github
- [SmartFoxServer入门]服务器安装
- 廖雪峰老师的git在线教程
- YII中路径别名
- poj 2777 Count Color(线段树区区+染色问题)
- hadoop操作
- Centos 7安装oracle 11g R2问题及解决方法汇总
- [LeetCode] Find Pivot Index 寻找中枢点
- Java-ServletRequestListener-ServletRequestAttributeListener
- centos7之NFS使用
- 23个Python爬虫开源项目代码,让你一次学个够
- 神级程序员通过两句话带你完全掌握Python最难知识点——元类!
- idea导出可执行jar包
- 马婕 2014年MBA,mpacc备考 报刊宣读1 中国的电子商务(转)
- PHP文件引入
- Python——截取web网页长图
热门文章
- ArcGIS engine中Display类库 (局部刷新)
- sql-server 2005数据库文件恢复(检測到基于一致性的逻辑 I/O 错误)
- hdu1533Going Home KM算法
- ios代理的使用,正向传值,逆向传值
- windows 常见环境变量(%AppData%、%TEMP%、%TMP%)
- tomcat+nginx+redis实现均衡负载以及session共享
- es6 ----- export 和 import
- Mvc异步
- ajax关于主流中的异类:应对Opera(四)
- 微信小程序从零开始开发步骤(二)创建小程序页面