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

zz/2024/4/20 14:38:09

本文讲述了基于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

转载于:https://www.cnblogs.com/baifeilong/p/4580622.html


http://www.ngui.cc/zz/2336535.html

相关文章

自恢复保险丝选型参数详解

自恢复保险丝最大的优势是其在额定使用范围内&#xff0c;每次故障排除之后&#xff0c;不需要重新更换自恢复保险丝&#xff0c;可反复多次应用于电路中&#xff0c;有效节约维修时间和降低成本。自恢复保险丝对电流和温度尤其地敏感&#xff0c;其电阻会随着电流和温度的增大…

硬件学习 (13)

一、内容 进行硬件学习。 二、知识点 1.方波有霍尔和无霍尔的相同与区别 (1)相同点 ①电源 这是一个降压模块&#xff0c;其中最主要的就是LM2576HV-ADJ元器件&#xff0c;它是开关型降压稳压器&#xff0c;共有五个引脚&#xff0c;分别是VIN、ON/OFF、GND、FEEDBACK和OUTPUT…

Redis技术分析及应用 windows、linux安装最新redis

标题redis基础windows下载地址好像是第一次登录需要指定密码之后就记住了linux下载地址配置全局环境变量下面且切记 已经启动不能关 另外开一个窗口redis启动时的几个报警错误临时使用推荐这三条命令&#xff1a;下面都是基于windows操作的redis基础 windows下载地址 安装教程…

实现微信登录

标题微信开放平台文档 第一步&#xff1a;请求CODE 第三方使用网站应用授权登录前请注意已获取相应网页授权作用域&#xff08;scopesnsapi_login&#xff09;&#xff0c;则可以通过在PC端打开以下链接&#xff1a; https://open.weixin.qq.com/connect/qrconnect?appidAPPI…

面试 --- java 设计模式

** 简述 设计模式 七大原则 ** 1.开发封闭原则 &#xff1a; 对扩展开放 &#xff0c; 对修改封闭。 在程序需要就行扩展的时候 &#xff0c; 不能去修改 原有的代码&#xff0c; 形成一个 热插拔 的效果。2.单一职责原则&#xff1a; 一个类 接口 或 方法 只负责 一个 …

springboot+dubbo实现项目itrip-search-consumer流程

准备 把3个solr的工具类&#xff08;BaseQuery、Param、SolrParam&#xff09;放到itrip-common中&#xff0c;导如下依赖&#xff1a; <!--solr导这个包&#xff0c;注意版本号为2.1.1--><dependency><groupId>org.springframework.data</groupId>&…

自恢复保险丝的各个参数含义

在项目中发现&#xff1a;自恢复保险丝存在一定的压差&#xff0c;这在一些电源要求较高的环境下要慎重选择。 自恢复保险丝工作原理 当有异常过电流通过自恢复保险丝时&#xff0c;产生的热量使高分子有机聚合物膨胀&#xff0c;包裹在高分子有机聚合物的导电粒子会分开&…

如何使用Java来发送企业邮箱,并设计收到邮件的样式

最近再给公司写一个发送邮件的系统&#xff0c;收到同事反馈说收到的默认样式太丑了&#xff0c;于是我钻研了一下&#xff0c;设计了一下自己的邮箱前端样式&#xff0c;于是特此写下这篇文章 一.搭建环境 相信大家也在其余的博客中学习到了怎么使用qq邮箱来发送邮件&#xff…

java入门-springboot+mybatis+vue实现简单的后台管理系统

源码地址:https://codechina.csdn.net/wwwzhouzy/vueadmin 注意:前后端在一个地址&#xff0c;vueClient是vue前端&#xff0c;zhouzyServer是后端 一、效果图 1、列表 2、图表 二、代码 此处贴核心代码 后端 代码结构&#xff1a; 1、pom引包 因为我用的是mysql5.0&…

CentOS7 搭建 iSCSI网络存储

服务器端安装配置//安装iscsi服务 #yum -y install targetcli//创建目录以便于设置共享 #mkdir /iscsi_iso//进入进入管理控制台 #targetcli /> cd backstores/fileio /backstores/fileio> create iso /iscsi_iso/iso.img 2G Created fileio iso with size 214748364…