springmvc+shiro+freemarker实现的安全及权限管理

本文讲述了基于springmvc+shiro实现安全管理,shiro+freemarker实现权限验证。

首先我们从web.xml开始:

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="2.5" xmlns
="http://java.sun.com/xml/ns/javaee"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
5 <context-param>
6 <param-name>contextConfigLocation</param-name>
7 <param-value>
8 classpath:resources/tag-context.xml
9 classpath:resources/shiro-context.xml
10 </param-value>
11 </context-param>
12 <!-- log4j -->
13 <context-param>

14 <param-name>log4jConfigLocation</param-name>
15 <param-value>classpath:resources/log4j.properties</param-value>
16 </context-param>
17 <context-param>
18 <param-name>log4jDelay</param-name>
19 <param-value>10000</param-value>
20 </context-param>
21 <!-- Spring -->
22 <listener>
23 <listener-class>
24 org.springframework.web.context.ContextLoaderListener
25 </listener-class>
26 </listener>
27 <listener>
28 <listener-class>
29 com.itrip.rp.listener.InitConfigListener
30 </listener-class>
31 </listener>
32 <!-- log4j -->
33 <listener>
34 <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
35 </listener>
36 <!-- 日志记录过滤器 -->
37 <filter>
38 <filter-name>requestLogFilter</filter-name>
39 <filter-class>
40 com.itrip.rp.filter.RequestLogFilter
41 </filter-class>
42 </filter>
43 <filter-mapping>
44 <filter-name>requestLogFilter</filter-name>
45 <url-pattern>/*</url-pattern>
46 </filter-mapping>
47 <!-- 编码过滤 -->
48 <filter>
49 <filter-name>encoding</filter-name>
50 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
51 <init-param>
52 <param-name>encoding</param-name>
53 <param-value>UTF-8</param-value>
54 </init-param>
55 </filter>
56 <filter-mapping>
57 <filter-name>encoding</filter-name>
58 <url-pattern>/*</url-pattern>
59 </filter-mapping>
60 <!-- shiro 安全过滤器 -->
61 <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml --> 62 <filter>
63 <filter-name>shiroFilter</filter-name>
64 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
65 <async-supported>true</async-supported>
66 <init-param>
67 <param-name>targetFilterLifecycle</param-name>
68 <param-value>true</param-value>
69 </init-param>
70 </filter>
71 <filter-mapping>
72 <filter-name>shiroFilter</filter-name>
73 <url-pattern>/*</url-pattern>
74 </filter-mapping>
75 <!-- SpringMVC -->
76 <servlet> 77 <servlet-name>resourcePlatform</servlet-name>
78 <servlet-class>
79 org.springframework.web.servlet.DispatcherServlet
80 </servlet-class>
81 <init-param>
82 <param-name>contextConfigLocation</param-name>
83 <param-value>
84 classpath:resources/applicationContext-*.xml
85 </param-value>
86 </init-param> 87 <load-on-startup>1</load-on-startup> 88 </servlet>
89 <servlet-mapping>
90 <servlet-name>resourcePlatform</servlet-name>
91 <url-pattern>/*</url-pattern>
92 </servlet-mapping>
93 </web-app>
复制代码

按照web.xml初始化顺序依次为:context-param--->listener--->filter--->servlet

tag-context.xml中主要配置了权限验证标签,代码如下:

复制代码
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
5 default-lazy-init="true">
6 <!--后台权限标签-->
7 <bean id="perm" class="com.itrip.rp.core.permission.PermissionDirective"/>
8 </beans>
复制代码

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

复制代码
 1 package com.itrip.rp.core.permission;
2
3 import java.io.IOException;
4 import java.util.Map;
5
6 import org.apache.shiro.SecurityUtils;
7 import org.apache.shiro.subject.Subject;
8
9 import com.itrip.rp.common.Constants;
10 import com.itrip.rp.core.freemarker.DirectiveUtils;
11
12 import freemarker.core.Environment;
13 import freemarker.template.TemplateDirectiveBody;
14 import freemarker.template.TemplateDirectiveModel;
15 import freemarker.template.TemplateException;
16 import freemarker.template.TemplateModel;
17
18 /**
19 * 后台管理员权限许可
20 *
21 * @author Benny
22 */
23 public class PermissionDirective implements TemplateDirectiveModel {
24
25 /***
26 * 权限验证
27 */
28 @SuppressWarnings("unchecked")
29 public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
30 String url = DirectiveUtils.getString(Constants.PARAM_URL, params);
31 Subject subject = SecurityUtils.getSubject();
32 boolean pass = subject.isPermitted(url);
33 if (pass) {
34 body.render(env.getOut());
35 }
36 }
37 }
复制代码
Constants.PARAM_URL="url"; //对应的值就是取freemarker标签中的url

标签形式如:

<@perm url="/product/add"></@perm>
Subject subject = SecurityUtils.getSubject(); //这一步是基于shiro获取认证用户对象
boolean pass = subject.isPermitted(url); //这一步就是进行权限验证,权限验证通过返回true,反之返回false

这里还是非常简单的。

接下来让我们看看shiro的具体配置吧,还是先看源代码:


复制代码
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 5 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
6 default-lazy-init="true">
7 <!-- Shiro拦截器 -->
8 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 9 <property name="securityManager" ref="securityManager" />
10 <property name="loginUrl" value="/login" />
11 <property name="successUrl" value="/index" />
12 <property name="filters">
13 <util:map>
14 <entry key="authc" value-ref="authcFilter" />
15 <entry key="user" value-ref="userFilter" />
16 <entry key="logout" value-ref="logoutFilter" />
17 </util:map>
18 </property>
19 <!--authc登陆认证 user用户认证检查 logout退出 filter-->
20 <property name="filterChainDefinitions">
21 <value>
22 /css/** = anon
23 /img/** = anon
24 /js/** = anon
25 /favicon.ico = anon
26 /login = authc
27 /logout = logout
28 /** = user
29 </value>
30 </property>
31 </bean>
32 <!-- 认证filter -->
33 <bean id="authcFilter" class="com.itrip.rp.core.security.AdminAuthenticationFilter"> 34 <property name="adminLogin" value="/login"/> 35 <property name="adminIndex" value="/index"/> 36 </bean>
37 <!-- 用户检查filter -->
38 <bean id="userFilter" class="com.itrip.rp.core.security.AdminUserFilter"/>
39 <!-- 退出系统filter -->
40 <bean id="logoutFilter" class="com.itrip.rp.core.security.AdminLogoutFilter">
41 <property name="logoutUrl" value="/login"/>
42 </bean>
43 <!-- 安全管理器 -->
44 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
45 <property name="realm" ref="authorizingRealm" />
46 <property name="sessionManager" ref="sessionManager"/>
47 <property name="cacheManager" ref="shiroEhcacheManager"/>
48 </bean>
49 <!-- 自定义登陆验证 -->
50 <bean id="authorizingRealm" class="com.itrip.rp.core.security.AdminAuthorizingRealm">
51 <property name="credentialsMatcher"> 52 <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
53 <!-- 密码加密方式 -->
54 <property name="hashAlgorithmName" value="MD5"/>
55 <!-- true means hex encoded, false means base64 encoded -->
56 <property name="storedCredentialsHexEncoded" value="true"/>
57 <!-- 迭代次数 -->
58 <property name="hashIterations" value="1" />
59 </bean>
60 </property>
61 </bean>
62 <!-- 缓存管理 -->
63 <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
64 <property name="cacheManagerConfigFile"> 65 <value>classpath:resources/ehcache-shiro.xml</value>
66 </property>
67 </bean>
68 <!-- 会话Cookie 180000-->
69 <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">

70 <constructor-arg value="sid"/>
71 <property name="httpOnly" value="true"/>
72 <property name="maxAge" value="180000"/>
73 </bean>
74 <!-- 会话ID生成器 -->
75 <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
76 <!-- 会话DAO -->
77 <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
78 <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
79 <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
80 </bean>
81 <!-- 会话管理器 -->
82 <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
83 <property name="globalSessionTimeout" value="1800000"/>
84 <property name="deleteInvalidSessions" value="true"/>
85 <property name="sessionValidationSchedulerEnabled" value="true"/>
86 <property name="sessionDAO" ref="sessionDAO"/>
87 <property name="sessionIdCookieEnabled" value="true"/>
88 <property name="sessionIdCookie" ref="sessionIdCookie"/>
89 </bean>
90 <!-- Shiro生命周期处理器-->
91 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
92 </beans>
复制代码

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

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?>
2 <ehcache>
3 <diskStore path="java.io.tmpdir/rp-shiro-ehcache"/>
4 <defaultCache
5 maxElementsInMemory="10000"
6 eternal="false"
7 timeToIdleSeconds="120"
8 timeToLiveSeconds="120"
9 overflowToDisk="true"
10 diskSpoolBufferSizeMB="30"
11 maxElementsOnDisk="10000000"
12 diskPersistent="false"
13 diskExpiryThreadIntervalSeconds="120"/>
14 <cache name="shiro-activeSessionCache"
15 maxElementsInMemory="10000"
16 overflowToDisk="true"
17 eternal="true"
18 timeToLiveSeconds="0"
19 timeToIdleSeconds="0"
20 diskPersistent="true"
21 diskExpiryThreadIntervalSeconds="600"/>
22
23 <cache name="org.apache.shiro.realm.text.PropertiesRealm-0-accounts"
24 maxElementsInMemory="1000"
25 eternal="true"
26 overflowToDisk="true"/>
27 </ehcache>
复制代码
1 /css/** = anon
2 /img/** = anon
3 /js/** = anon
4 /favicon.ico = anon

这里是对静态资源的处理,静态资源不做认证。

重要的是以下三个拦截器,分别实现了用户认证,用户检查及退出系统过程。

复制代码
1 <property name="filters">
2     <util:map>
3         <entry key="authc" value-ref="authcFilter" />
4         <entry key="user" value-ref="userFilter" />
5         <entry key="logout" value-ref="logoutFilter" />
6     </util:map>
7 </property>
复制代码

先看看用户认证authcFilter吧:

复制代码
  1 package com.itrip.rp.core.security;
2
3 import java.util.Date;
4
5 import javax.servlet.ServletRequest;
6 import javax.servlet.ServletResponse;
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpServletResponse;
9
10 import org.apache.shiro.authc.AuthenticationToken;
11 import org.apache.shiro.subject.Subject;
12 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
13 import org.apache.shiro.web.util.WebUtils;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.itrip.rp.common.Constants;
18 import com.itrip.rp.entity.beans.UserBaseInfo;
19 import com.itrip.rp.exception.AuthenticationException;
20 import com.itrip.rp.exception.DisabledException;
21 import com.itrip.rp.exception.UsernameNotFoundException;
22 import com.itrip.rp.service.AuthenticationService;
23 import com.itrip.rp.service.LogService;
24 import com.itrip.rp.service.UserService;
25 import com.itrip.rp.session.SessionProvider;
26 import com.itrip.rp.utils.DateFormatUtils;
27 import com.itrip.rp.utils.RequestUtils;
28 import com.itrip.rp.utils.SpringContextUtil;
29
30 /**
31 * 自定义登陆认证filter
32 *
33 * @author Benny
34 */
35 public class AdminAuthenticationFilter extends FormAuthenticationFilter {
36
37 private Logger logger = LoggerFactory.getLogger("security");
38
39 /**
40 * 执行登陆操作
41 */
42 @Override
43 protected boolean executeLogin(ServletRequest request, ServletResponse response) {
44 AuthenticationToken token = createToken(request, response);
45 if (token == null) {
46 String msg = "create AuthenticationToken error";
47 throw new IllegalStateException(msg);
48 }
49 String username = (String) token.getPrincipal();
50 UserBaseInfo user = userService.login(username);
51 if (user != null) {
52 if (!user.getStatus()) {
53 // 用户禁用
54 return onLoginFailure(username, token, new DisabledException(), request, response);
55 }
56 } else {
57 // 用户名不存在
58 return onLoginFailure(username, token, new UsernameNotFoundException(), request, response);
59 }
60 try {
61 Subject subject = getSubject(request, response);
62 subject.login(token);
63 return onLoginSuccess(user, token, subject, request, response);
64 } catch (Exception e) {
65 // TODO Auto-generated catch block
66 return onLoginFailure(username, token, new AuthenticationException(), request, response);
67 }
68 }
69
70 /**
71 * 初始化service及登陆跳转
72 */
73 @Override
74 public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
75 if (userService == null) {
76 userService = (UserService) SpringContextUtil.getBean(UserService.class);
77 }
78 if (logService == null) {
79 logService = (LogService) SpringContextUtil.getBean(LogService.class);
80 }
81 if (authService == null) {
82 authService = (AuthenticationService) SpringContextUtil.getBean(AuthenticationService.class);
83 }
84 if (session == null) {
85 session = (SessionProvider) SpringContextUtil.getBean(SessionProvider.class);
86 }
87 boolean isAllowed = isAccessAllowed(request, response, mappedValue);
88 // 登陆跳转
89 if (isAllowed && isLoginRequest(request, response)) {
90 try {
91 issueSuccessRedirect(request, response);
92 } catch (Exception e) {
93 logger.error("", e);
94 }
95 return false;
96 }
97 return isAllowed || onAccessDenied(request, response, mappedValue);
98 }
99
100 @Override
101 protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
102 HttpServletRequest req = (HttpServletRequest) request;
103 HttpServletResponse res = (HttpServletResponse) response;
104 String successUrl = getAdminIndex() != null ? getAdminIndex() : super.getSuccessUrl();
105 WebUtils.redirectToSavedRequest(req, res, successUrl);
106 }
107
108 @Override
109 protected boolean isLoginRequest(ServletRequest req, ServletResponse resp) {
110 String loginUrl = getAdminLogin() != null ? getAdminLogin() : super.getLoginUrl();
111 return pathsMatch(loginUrl, req);
112 }
113
114 /**
115 * 登陆成功
116 */
117 private boolean onLoginSuccess(UserBaseInfo user, AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) 118 throws Exception {
119 HttpServletRequest req = (HttpServletRequest) request;
120 HttpServletResponse res = (HttpServletResponse) response;
121 // 记录用户登陆信息
122 authService.login(user, RequestUtils.getIpAddr(req), req, res, session);
123 // 将系统当前登陆用户信息放入session
124 session.setAttribute(req, Constants.USERNAME, user.getNickName());
125 Date lastLogin = authService.findSecond(user.getUserId());
126 if (lastLogin != null) {
127 session.setAttribute(req, Constants.LAST_LOGIN_TIME, DateFormatUtils.format(lastLogin, "yyyy-MM-dd HH:mm:ss"));
128 }
129 logService.loginSuccess(req, user.getUserId(), "login.log.loginSuccess"); 130 return super.onLoginSuccess(token, subject, request, response);
131 }
132
133 /**
134 * 登陆失败
135 */
136 private boolean onLoginFailure(String username, AuthenticationToken token, AuthenticationException e, ServletRequest request,
137 ServletResponse response) {
138 HttpServletRequest req = (HttpServletRequest) request;
139 logService.loginFailure(req, "login.log.loginFailure", "userName=" + username);
140 request.setAttribute(Constants.MESSAGE, e.getMessage());
141 return super.onLoginFailure(token, e, request, response);
142 }
143
144 private UserService userService;
145 private LogService logService;
146 private SessionProvider session;
147 private AuthenticationService authService;
148
149 private String adminIndex;
150
151 private String adminLogin;
152
153 public String getAdminIndex() {
154 return adminIndex;
155 }
156 157 public void setAdminIndex(String adminIndex) {
158 this.adminIndex = adminIndex;
159 }
160
161 public String getAdminLogin() {
162 return adminLogin;
163 }
164
165 public void setAdminLogin(String adminLogin) {
166 this.adminLogin = adminLogin;
167 }
168 }
复制代码

需要说明的就是service的获取,在filter中获取spring自动注入bean需要通过spring上下文来获取,这里定义实现了获取spring上下文及注入bean的工具类SpringContextUtil.java稍后会详细说明这个类以及配置。

用户登录操作“/login”交由authcFilter处理,登录controller代码很简单:

复制代码
 1 package com.itrip.rp.controller;
2
3 import javax.servlet.http.HttpServletRequest;
4 import javax.servlet.http.HttpServletResponse;
5
6 import org.apache.commons.lang.StringUtils;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9 import org.springframework.stereotype.Controller;
10 import org.springframework.ui.ModelMap;
11 import org.springframework.web.bind.annotation.RequestMapping;
12
13 import com.itrip.rp.common.Constants;
14 import com.itrip.rp.controller.base.BaseController;
15
16 /**
17 * 系统登陆Controller
18 *
19 * @author Benny
20 *
21 */
22 @Controller
23 public class LoginController extends BaseController {
24
25 protected static final Logger LOG = LoggerFactory.getLogger("run");
26
27 /**
28 * 登陆
29 *
30 * @param request
31 * @param response
32 * @param model
33 * @return
34 */
35 @RequestMapping(value = "/login")
36 public String login(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
37 return "login";
38 }
39
40 /**
41 * 系统首页
42 *
43 * @param message
44 * @param request
45 * @param response
46 * @param model
47 * @return
48 */
49 @RequestMapping(value = "/index")
50 public String index(String message, HttpServletRequest request, HttpServletResponse response, ModelMap model) {
51 if (!StringUtils.isBlank(message)) {
52 model.addAttribute(Constants.MESSAGE, message);
53 }
54 return "product/index";
55 }
56 }
复制代码

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

复制代码
 1 <!doctype html>
2 <body>
3 <form name="jvForm" action="${accessRoot}/login" method="post">
4 <div class="loginbox round15">
5 <dl>
6 <dd class="fz24 pt30">User login</dd>
7 <dd>
8 <input type="text" placeholder="Login name" autocomplete="off" name="username">
9 </dd>
10 <dd>
11 <input type="password" placeholder="Password" autocomplete="off" name="password" title="click enter login">
12 </dd>
13 <dd>
14 <a href="javascript:document.jvForm.submit();" class="login-bt round3" id="btnLogin">Login</a>
15 </dd>
16 <font color="red" id="errTip">${message!}</font>
17 </dl>
18 </div>
19 </form>
20 </body>
21 </html>
复制代码

用户认证检查userFilter:

复制代码
 1 package com.itrip.rp.core.security;
2
3 import java.io.IOException;
4
5 import javax.servlet.ServletRequest;
6 import javax.servlet.ServletResponse;
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpServletResponse;
9
10 import org.apache.shiro.web.filter.authc.UserFilter;
11 import org.apache.shiro.web.util.WebUtils;
12
13 /**
14 * 用户认证检查filter
15 *
16 * @author Benny
17 */
18 public class AdminUserFilter extends UserFilter {
19 // 未登陆重定向到登陆页
20 protected void redirectToLogin(ServletRequest req, ServletResponse resp) throws IOException { 21 HttpServletRequest request = (HttpServletRequest) req;
22 HttpServletResponse response = (HttpServletResponse) resp;
23 WebUtils.issueRedirect(request, response, getLoginUrl());
24 }
25 }

复制代码

最后是退出系统logoutFilter:

复制代码
 1 package com.itrip.rp.core.security;
2
3 import javax.servlet.ServletRequest;
4 import javax.servlet.ServletResponse;
5 import javax.servlet.http.HttpServletRequest;
6
7 import org.apache.commons.lang.StringUtils;
8 import org.apache.shiro.subject.Subject;
9 import org.apache.shiro.web.filter.authc.LogoutFilter;
10
11 import com.itrip.rp.common.Constants;
12
13 /**
14 * 退出系统 filter
15 *
16 * @author Benny
17 */
18 public class AdminLogoutFilter extends LogoutFilter {
19
20 @Override
21 protected String getRedirectUrl(ServletRequest req, ServletResponse resp, Subject subject) {
22 HttpServletRequest request = (HttpServletRequest) req;
23 String redirectUrl = request.getParameter(Constants.RETURN_URL);
24 if (StringUtils.isBlank(redirectUrl)) {
25 redirectUrl = getLogoutUrl();
26 if (StringUtils.isBlank(redirectUrl)) {
27 redirectUrl = getRedirectUrl();
28 }
29 }
30 return redirectUrl;
31 }
32
33 private String logoutUrl;
34
35 public void setLogoutUrl(String logoutUrl) {
36 this.logoutUrl = logoutUrl;
37 }
38
39 public String getLogoutUrl() {
40 return logoutUrl;
41 }
42 }
复制代码

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

复制代码
 1 package com.itrip.rp.core.security;
2
3 import java.util.HashSet;
4 import java.util.List;
5 import java.util.Set;
6
7 import org.apache.shiro.authc.AuthenticationException;
8 import org.apache.shiro.authc.AuthenticationInfo;
9 import org.apache.shiro.authc.AuthenticationToken;
10 import org.apache.shiro.authc.SimpleAuthenticationInfo;
11 import org.apache.shiro.authc.UsernamePasswordToken;
12 import org.apache.shiro.authz.AuthorizationInfo;
13 import org.apache.shiro.authz.SimpleAuthorizationInfo;
14 import org.apache.shiro.realm.AuthorizingRealm;
15 import org.apache.shiro.subject.PrincipalCollection;
16 import org.apache.shiro.subject.SimplePrincipalCollection;
17 import org.apache.shiro.util.CollectionUtils;
18
19 import com.itrip.rp.entity.beans.UserBaseInfo;
20 import com.itrip.rp.service.UserService;
21 import com.itrip.rp.utils.SpringContextUtil;
22
23 /**
24 * 认证及授权Realm
25 *
26 * @author Benny
27 */
28 public class AdminAuthorizingRealm extends AuthorizingRealm { 29
30 /**
31 * 登陆认证
32 */
33 @Override 34 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
35 UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
36 if (userService == null) {
37 userService = (UserService) SpringContextUtil.getBean(UserService.class);
38 }
39 UserBaseInfo user = userService.login(token.getUsername());
40 if (user != null) {
41 return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
42 } else {
43 return null;
44 }
45 }
46
47 /**
48 * 授权
49 */
50 @Override
51 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
52 // TODO Auto-generated method stub
53 UserBaseInfo user = (UserBaseInfo) principals.getPrimaryPrincipal();
54 SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
55 if (user != null) {
56 if (userService == null) {
57 userService = (UserService) SpringContextUtil.getBean(UserService.class);
58 }
59 List<String> perms = userService.getPerms(user.getUserId());
60 Set<String> set = new HashSet<String>(perms);
61 if (!CollectionUtils.isEmpty(perms)) {
62 // 权限加入AuthorizationInfo认证对象
63 auth.setStringPermissions(set);
64 }
65 }
66 return auth;
67 }
68
69 /**
70 * 清空用户权限缓存
71 *
72 * @param username
73 */
74 public void removeUserAuthorizationInfoCache(String username) {
75 SimplePrincipalCollection pc = new SimplePrincipalCollection();
76 pc.add(username, super.getName());
77 super.clearCachedAuthorizationInfo(pc);
78 }
79
80 private UserService userService;
81 }
复制代码

登录认证成功后将用户对象放入AuthenticationInfo中,以便授权过程中直接使用用户对象。

授权操作只有在第一次进行权限验证的时候才会初始化(比较重要),将用户所拥有的所有权限放入AuthorizationInfo对象。

当用户权限发生变化,就需要手动调用removeUserAuthorizationInfoCache方法去清除用户权限缓存。

最后再看看springmvc配置文件:

复制代码
  1 <beans xmlns="http://www.springframework.org/schema/beans"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:mvc="http://www.springframework.org/schema/mvc"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xmlns:jdbc="http://www.springframework.org/schema/jdbc"
6 xmlns:context="http://www.springframework.org/schema/context"
7 xmlns:tx="http://www.springframework.org/schema/tx"
8 xsi:schemaLocation="http://www.springframework.org/schema/beans
9 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
10 http://www.springframework.org/schema/context
11 http://www.springframework.org/schema/context/spring-context-3.0.xsd
12 http://www.springframework.org/schema/tx
13 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
14 http://www.springframework.org/schema/jdbc
15 http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
16 http://www.springframework.org/schema/aop
17 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
18 http://www.springframework.org/schema/mvc
19 http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
20 21 <!-- 自动依赖注入 -->
22 <context:component-scan base-package="com.itrip.rp" />
23
24 <!-- 文件上传解析器 id 必须为multipartResolver -->
25 <bean id="multipartResolver"
26 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
27 <property name="maxUploadSize" value="10485760" />
28 </bean>
29
30 <!-- 没有自定义实现拦截器的时候必须声明spring默认配置 -->
31 <!-- <mvc:annotation-driven/> -->
32 <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
33 <property name="interceptors">
34 <list>
35 <ref bean="adminContextInterceptor"/> 36 </list>
37 </property>
38 </bean>
39 <bean id="adminContextInterceptor" class="com.itrip.rp.interceptor.AdminContextInterceptor">
40 <property name="excludeUrls">
41 <list>
42 <value>/login</value>
43 <value>/logout</value>
44 </list>
45 </property>
46 </bean>
47
48 <!-- 静态资源 -->
49 <mvc:resources location="/img/" mapping="/img/**" />
50 <mvc:resources location="/js/" mapping="/js/**" />
51 <mvc:resources location="/css/" mapping="/css/**" />
52
53 <!-- @responsebody标签返回对象格式配置 -->
54 <bean
55 class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
56 <!-- 配置信息转换,将用@responsebody注解的返回值转换为json返回前台,编码为utf-8 -->
57 <property name="messageConverters">
58 <list>
59 <bean
60 class="org.springframework.http.converter.StringHttpMessageConverter">
61 <property name="supportedMediaTypes">
62 <list>
63 <value>text/html;charset=UTF-8</value
>
64 </list>
65 </property>
66 </bean>
67 <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
68 <property name="supportedMediaTypes">
69 <list>
70 <value>application/json;charset=UTF-8</value>
71 </list>
72 </property>
73 </bean>
74 </list>
75 </property>
76 </bean>
77 <!-- 异常处理 -->
78 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
79 <property name="exceptionMappings">
80 <props>
81 <prop key="java.lang.Exception">error/404</prop>
82 <prop key="java.lang.Throwable">error/404</prop>
83 </props>
84 </property>
85 <property name="warnLogCategory" value="WARN" />
86 <property name="defaultErrorView" value="error/404" />
87 </bean>
88
89 <!-- 默认视图配置welcome页 -->
90 <mvc:view-controller path="/" view-name="product/index"/>
91
92 <!-- freemarker视图解析器配置 -->
93 <bean id="freemarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
94 <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
95 <!-- 视图名后缀 -->
96 <property name="suffix" value=".html" />
97 <property name="contentType" value="text/html; charset=UTF-8" />
98 <!-- request/session==true请求和会话属性都被复制到模板的属性集中,此时spring必须设置为true -->
99 <property name="exposeRequestAttributes" value="false" />
100 <property name="exposeSessionAttributes" value="false" />
101 <property name="exposeSpringMacroHelpers" value="true" />
102 </bean>
103 <bean id="freemarkerConfig"
104 class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
105 <!-- 模板路径 -->
106 <property name="templateLoaderPath" value="/view/" />
107 <property name="freemarkerVariables">
108 <map>
109 <!--后台管理权限控制 -->
110 <entry key="perm" value-ref="perm" />
111 </map>
112 </property>
113 <property name="freemarkerSettings">
114 <props>
115 <prop key="template_update_delay">0</prop>
116 <prop key="defaultEncoding">UTF-8</prop>
117 <prop key="url_escaping_charset">UTF-8</prop>
118 <prop key="locale">zh_CN</prop>
119 <prop key="boolean_format">true,false</prop>
120 <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
121 <prop key="date_format">yyyy-MM-dd</prop>
122 <prop key="time_format">HH:mm:ss</prop>
123 <prop key="number_format">0.######</prop>
124 <prop key="whitespace_stripping">true</prop>
125 </props>
126 </property> 127 </bean>

128 <!-- session持有者 -->
129 <bean id="sessionProvider" class="com.itrip.rp.session.HttpSessionProvider" />
130 <!-- spring上下文工具类 -->
131 <bean id="springContextUtil " class="com.itrip.rp.utils.SpringContextUtil" />
132 <!-- 数据库配置 -->
133 <import resource="classpath:resources/database-context.xml"/>
134 </beans>
复制代码

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

复制代码
 1 package com.itrip.rp.utils;
2
3 import org.springframework.beans.BeansException;
4 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
5 import org.springframework.context.ApplicationContext;
6 import org.springframework.context.ApplicationContextAware;
7
8 /**
9 * spring上下文工具类
10 *
11 * @author Benny
12 *
13 */
14 public class SpringContextUtil implements ApplicationContextAware {
15
16 private static ApplicationContext applicationContext; // Spring应用上下文环境
17
18 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
19 SpringContextUtil.applicationContext = applicationContext;
20 }
21
22 public static ApplicationContext getApplicationContext() {
23 return applicationContext;
24 }
25
26 public static Object getBean(String name) throws BeansException {
27 return applicationContext.getBean(name);
28 }
29
30 public static Object getBean(Class<?> requiredType) throws BeansException {
31 return applicationContext.getBean(requiredType);
32 }
33
34 public static Object getBean(String name, Class<?> requiredType) throws BeansException {
35 return applicationContext.getBean(name, requiredType);
36 }
37
38 public static boolean containsBean(String name) {
39 return applicationContext.containsBean(name);
40 }
41
42 public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
43 return applicationContext.isSingleton(name);
44 }
45
46 public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
47 return applicationContext.getType(name);
48 }
49
50 public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
51 return applicationContext.getAliases(name);
52 }
53 }
复制代码

至此,springmvc+shiro安全管理+freemarker标签权限验证就完成了。

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

复制代码
 1 package com.itrip.rp.interceptor;
2
3 import javax.servlet.http.HttpServletRequest;
4 import javax.servlet.http.HttpServletResponse;
5
6 import org.apache.shiro.SecurityUtils;
7 import org.apache.shiro.subject.Subject;
8 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
9 import org.springframework.web.util.UrlPathHelper;
10
11 /**
12 * URI拦截器 用户权限验证
13 *
14 * @author Benny
15 */
16 public class AdminContextInterceptor extends HandlerInterceptorAdapter { 17
18 @Override
19 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
20 // 获取请求链接
21 String uri = getURI(request);
22 // 排除例外URI,例如:登陆、退出
23 if (exclude(uri)) {
24 return true;
25 }
26 Subject subject = SecurityUtils.getSubject();
27 boolean pass = subject.isPermitted(uri);
28 if (pass) {
29 return true;
30 } else {
31 // 跳转至异常处理
32 throw new Exception();
33 }
34 }
35 36 /**
37 * 判断是否例外uri
38 *
39 * @param uri
40 * @return
41 */
42 private boolean exclude(String uri) {
43 if (excludeUrls != null) {
44 for (String exc : excludeUrls) {
45 // 允许以excludeurl结尾的请求
46 if (uri.endsWith(exc)) {
47 return true;
48 }
49 }
50 }
51 return false;
52 }
53
54 /**
55 * 获取请求URL
56 *
57 * @param request
58 * @author Benny
59 * @return
60 */
61 private static String getURI(HttpServletRequest request) {
62 UrlPathHelper helper = new UrlPathHelper();
63 return helper.getOriginatingRequestUri(request);
64 }
65
66 private String[] excludeUrls;
67
68 public void setExcludeUrls(String[] excludeUrls) {
69 this.excludeUrls = excludeUrls;
70 }
71 }
复制代码

以上实现了shiro安全管理+freemarker标签式的权限控制+系统全局url权限控制,基本满足大部分web项目的权限管理。

到此结束!

 使用的jar包以及版本在此说明一下:

shiro相关jar包:

复制代码
 1 <!-- shiro配置start -->
2 <dependency>
3 <groupId>org.apache.shiro</groupId>
4 <artifactId>shiro-web</artifactId>
5 <version>1.2.2</version>
6 </dependency>
7
8 <dependency> 9 <groupId>org.apache.shiro</groupId> 10 <artifactId>shiro-ehcache</artifactId> 11 <version>1.2.2</version> 12 </dependency> 13
14 <dependency> 15 <groupId>org.apache.shiro</groupId>
16 <artifactId>shiro-quartz</artifactId>
17 <version>1.2.2</version>
18 </dependency>
19 <dependency>
20 <groupId>org.apache.shiro</groupId>
21 <artifactId>shiro-spring</artifactId>
22 <version>1.2.2</version>
23 </dependency>
24 <!-- shiro配置end -->
复制代码

spring使用版本为3.0.5

0
0
« 上一篇:jQuery异步提交form表单
» 下一篇:mybatis+springmvc缓存设置
	</div>
posted @ 2018-01-18 14:39  星朝  阅读(445)  评论(0编辑  收藏  举报