本文讲述了基于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实现的安全及权限管理的更多相关文章

  1. 基于shiro+jwt的真正rest url权限管理,前后端分离

    代码地址如下:http://www.demodashi.com/demo/13277.html bootshiro & usthe bootshiro是基于springboot+shiro+j ...

  2. 【shiro】(2)---基于RUL的权限管理

    基于RUL的权限管理 我想在写shiro权限管理认证前,先来一个基于URL实现的权限管理控制. 一.基于URI的权限业务逻辑  实现思路:       将系统操作的每个url配置在权限表中,将权限对应 ...

  3. Shiro集成SSM基于动态URL权限管理(二)

    这个案例基于上一个demo扩展而来.所以数据库表,在Shiro集成SSM基于URL权限管理(一)开篇的一致.如果上个demo操作的建议重新导入一次,避免出现问题. 而这次都不是通过固定写在方法上的注解 ...

  4. SpringMVC拦截器2(资源和权限管理)(作为补充说明)

    SpringMVC拦截器(资源和权限管理) 1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServle ...

  5. 《shiro》视频目录---1、权限管理-shiro

    \day01_shiro\0323\10realm支持散列.avi;\day01_shiro\0323\1权限管理原理.avi;\day01_shiro\0323\2权限管理解决方案.avi;\day ...

  6. Springmvc+Shiro实战

    原文链接:http://blog.csdn.net/qq_37936542/article/details/79010449 springmvc+shiro实现系统粗细粒度的权限管理步骤: 1:表格设 ...

  7. 7. 整合shiro,搭建粗粒度权限管理

    shiro是一个易用的权限管理框架,只需提供一个Realm即可在项目中使用,本文就将结合上一篇中搭建的权限模块.角色模块和用户模块来搭建一个粗粒度的权限管理系统,具体如下:1. 添加shiro依赖和与 ...

  8. springboot学习笔记:11.springboot+shiro+mysql+mybatis(通用mapper)+freemarker+ztree+layui实现通用的java后台管理系统(权限管理+用户管理+菜单管理)

    一.前言 经过前10篇文章,我们已经可以快速搭建一个springboot的web项目: 今天,我们在上一节基础上继续集成shiro框架,实现一个可以通用的后台管理系统:包括用户管理,角色管理,菜单管理 ...

  9. SpringMVC+Shiro权限管理【转】

    1.权限的简单描述 2.实例表结构及内容及POJO 3.Shiro-pom.xml 4.Shiro-web.xml 5.Shiro-MyShiro-权限认证,登录认证层 6.Shiro-applica ...

随机推荐

  1. 解决wget下载文件名乱码的一些方法

    在下载用apache或者nginx做的索引目录时,遇到文件名乱码问题.搜索了不少资料,尝试了好几种方案,大家可以结合使用. 一般情况下加上–restrict-file-names=nocontrol参 ...

  2. CentOS 7.0yum安装MySQL

    CentOS 7.0yum安装MySQL 1.下载mysql的repo源 $ wget http://repo.mysql.com/mysql-community-release-el7-5.noar ...

  3. pix格式的一些摸索

    作者:朱金灿 来源:http://blog.csdn.net/clever101 以前因为工作关系研究过PCI的系统格式pix,但是遗留了一些问题,最近又想重新解决这些问题.研究了一天,有些收获,但是 ...

  4. 【深入篇】Android常用布局方式简介

    LinearLayout 线性布局是程序中最常见的布局方式.一般分为水平线性布局和竖直线性布局,通过android.orientation属性可以设置线性布局的方向. 在布局中操作颜色时,要用的是十六 ...

  5. DG 参数详解

    1.与角色无关的参数 ◆ DB_UNIQUE_NAME:数据库唯一名.对于物理standby,DB_NAME必须相同,对于逻辑standby,DB_NAME可以不同,所以在10g中引入DB_UNIQU ...

  6. BZOJ3511: 土地划分(最小割)

    Description Y国有N座城市,并且有M条双向公路将这些城市连接起来,并且任意两个城市至少有一条路径可以互达. Y国的国王去世之后,他的两个儿子A和B都想成为新的国王,但他们都想让这个国家更加 ...

  7. 关于IDEA编译器在初次使用thymeleaf 引入无效 , 导致th无法使用的原因

    首先pom.xml里面要导入thymeleaf的依赖 然后在html中加入  xmlns:th="http://www.thymeleaf.org" 最后点击file ---> ...

  8. xml格式报文的拼装,和解析成实体类

    我们的微信支付,使用的是第三方的支付,某银行的微信支持渠道.所有的接口请求.应答都是xml格式报文,这样就需要用到xml格式报文的拼装和解析,这儿简单讲一下. 拼接xml格式报文. 从页面表单提交和配 ...

  9. POJ——T 2796 Feel Good

    http://poj.org/problem?id=2796 Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 15375   ...

  10. UVA 11346 Probability (几何概型, 积分)

    题目链接:option=com_onlinejudge&Itemid=8&page=show_problem&problem=2321">https://uva ...