最近做的项目需要加上权限控制, 最后选型用的是shiro,这个是用确实比较简单。配置文件说明
web.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app 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"
- version="2.5">
- <display-name>pscms</display-name>
- <!-- Spring ApplicationContext配置文件的路径,可使用通配符,多个路径用,号分隔
- 此参数用于后面的Spring Context Loader -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath*:/applicationContext*.xml
- </param-value>
- </context-param>
- <!-- 設定Spring Context的默认Profile -->
- <context-param>
- <param-name>spring.profiles.default</param-name>
- <param-value>production</param-value>
- </context-param>
- <!--Spring的ApplicationContext 载入 -->
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Filter 定义 -->
- <!-- Character Encoding filter -->
- <filter>
- <filter-name>encodingFilter</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>
- <init-param>
- <param-name>forceEncoding</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>encodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- Shiro Security filter-->
- <span style="background-color: #ffffff; color: #ff0000;"> <filter>
- <filter-name>shiroFilter</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- <init-param>
- <param-name>targetFilterLifecycle</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter></span>
- <filter-mapping>
- <filter-name>shiroFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- SiteMesh Web-Page Layout filter-->
- <filter>
- <filter-name>sitemeshFilter</filter-name>
- <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>sitemeshFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!-- Spring MVC Servlet -->
- <servlet>
- <servlet-name>springServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/spring-mvc.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>springServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- <servlet>
- <servlet-name>captcha</servlet-name>
- <servlet-class>com.surfilter.pscms.web.CaptchaController</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>captcha</servlet-name>
- <url-pattern>/captcha</url-pattern>
- </servlet-mapping>
- <!-- session超时定义,单位为分钟 -->
- <session-config>
- <session-timeout>20</session-timeout>
- </session-config>
- <!-- 出错页面定义 -->
- <error-page>
- <exception-type>java.lang.Throwable</exception-type>
- <location>/WEB-INF/views/error/500.jsp</location>
- </error-page>
- <error-page>
- <error-code>500</error-code>
- <location>/WEB-INF/views/error/500.jsp</location>
- </error-page>
- <error-page>
- <error-code>404</error-code>
- <location>/WEB-INF/views/error/404.jsp</location>
- </error-page>
- </web-app>
applicationContext-shiro.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.1.xsd"
- default-lazy-init="true">
- <description>Shiro Configuration</description>
- <!-- Shiro's main business-tier object for web-enabled applications -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="shiroDbRealm" />
- <property name="cacheManager" ref="cacheManager" />
- </bean>
- <!-- 項目自定义的Realm -->
- <bean id="shiroDbRealm" class="com.surfilter.pscms.service.account.ShiroDbRealm" depends-on="userDao,groupDao">
- <property name="accountManager" ref="accountManager"/>
- </bean>
- <!-- Shiro Filter -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <property name="securityManager" ref="securityManager" />
- <property name="loginUrl" value="/loginpw" />
- <property name="successUrl" value="/main/" />
- <property name="filters">
- <map>
- <entry key="authc" value-ref="authc"></entry>
- </map>
- </property>
- <property name="filterChainDefinitions">
- <value>
- /loginpw = authc
- /logoutlogout = logout
- /captcha = anon
- /static/** = anon
- /mobile/** = anon
- /** = user
- </value>
- </property>
- </bean>
- <bean id="authc" class="com.surfilter.pscms.service.captcha.CaptchaFormAuthenticationFilter"></bean>
- <!-- 用户授权信息Cache -->
- <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
- <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
- <!-- AOP式方法级权限检查 -->
- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
- <property name="proxyTargetClass" value="true" />
- </bean>
- <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
- <property name="securityManager" ref="securityManager"/>
- </bean>
- </beans>
spring-mvc.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"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
- <!-- 自动扫描且只扫描@Controller -->
- <context:component-scan base-package="com.surfilter.pscms" use-default-filters="false">
- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
- </context:component-scan>
- <bean id="permission" class="com.surfilter.pscms.entity.account.Permission" init-method="initialize"/>
- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
- <property name="messageConverters">
- <list>
- <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
- <bean class="org.springframework.http.converter.StringHttpMessageConverter">
- <property name="supportedMediaTypes">
- <list><value>text/plain;charset=UTF-8</value></list>
- </property>
- </bean>
- <bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
- <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
- <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" />
- <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" />
- </list>
- </property>
- </bean>
- <mvc:annotation-driven />
- <mvc:default-servlet-handler/>
- <!-- 定义首页 -->
- <mvc:view-controller path="/" view-name="redirect:/main/"/>
- <!-- 定义JSP -->
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/WEB-INF/views/"/>
- <property name="suffix" value=".jsp"/>
- </bean>
- <!-- View resolvers can also be configured with ResourceBundles or XML files.
- If you need different view resolving based on Locale, you have to use the
- resource bundle resolver. -->
- <!-- 这个是针对返回视图还是json值的视图配置 来分别处理同步和异步请求 -->
- <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
- <property name="mediaTypes">
- <map>
- <entry key="html" value="text/html" />
- <entry key="json" value="application/json" />
- </map>
- </property>
- <property name="favorParameter" value="true" />
- <property name="viewResolvers">
- <list>
- <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
- <bean id="viewResolver"
- class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
- <property name="cache" value="true" />
- <property name="prefix" value="" />
- <property name="suffix" value=".ftl" />
- <property name="contentType" value="text/html;charset=UTF-8"></property>
- <property name="requestContextAttribute" value="request" />
- <property name="exposeSpringMacroHelpers" value="true" />
- <property name="exposeRequestAttributes" value="true" />
- <property name="exposeSessionAttributes" value="true" />
- </bean>
- </list>
- </property>
- <property name="defaultContentType" value="text/html" />
- </bean>
- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <property name="maxUploadSize">
- <value>10485760</value>
- </property>
- <property name="maxInMemorySize">
- <value>5120</value>
- </property>
- </bean>
- <!-- 支持 Shiro对Controller的方法级AOP安全控制 begin-->
- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
- <property name="proxyTargetClass" value="true" />
- </bean>
- <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
- <property name="securityManager" ref="securityManager"/>
- </bean>
- <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
- <property name="exceptionMappings">
- <props>
- <prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
- </props>
- </property>
- </bean>
- <!-- end -->
- <!-- 初始化加载板块列表 -->
- <bean id="showBoards" class="com.surfilter.pscms.bean.ShowBoards" init-method="init"></bean>
- </beans>
ShiroDbRealm.java继承AuthorizingRealm 重新认证和鉴权的方法
- package com.surfilter.pscms.service.account;
- import java.io.Serializable;
- import org.apache.shiro.SecurityUtils;
- 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.authz.AuthorizationInfo;
- import org.apache.shiro.authz.SimpleAuthorizationInfo;
- import org.apache.shiro.cache.Cache;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.apache.shiro.subject.SimplePrincipalCollection;
- import org.springframework.beans.factory.annotation.Autowired;
- import com.surfilter.pscms.entity.account.User;
- import com.surfilter.pscms.service.captcha.CaptchaUsernamePasswordToken;
- import com.surfilter.pscms.service.captcha.IncorrectCaptchaException;
- /**
- * 自实现用户与权限查询. 演示关系,密码用明文存储,因此使用默认 的SimpleCredentialsMatcher.
- */
- public class ShiroDbRealm extends AuthorizingRealm {
- private AccountManager accountManager;
- /**
- * 认证回调函数, 登录时调用.
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(
- AuthenticationToken authcToken) throws AuthenticationException {
- CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken) authcToken;
- // 验证码 验证
- String captcha = null;
- Object obj_captcha = SecurityUtils.getSubject().getSession()
- .getAttribute("RandomCode");
- if (obj_captcha instanceof String)
- captcha = (String) obj_captcha;
- if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha())) {
- throw new IncorrectCaptchaException("验证码错误!");
- }
- // 用户名密码验证
- User user = accountManager.findUserByLoginName(token.getUsername());
- if (user != null) {
- SecurityUtils.getSubject().getSession().setAttribute(
- "loginUserInfo", user);
- return new SimpleAuthenticationInfo(new ShiroUser(user
- .getLoginName(), user.getName()), user.getPassword(),
- getName());
- } else {
- return null;
- }
- }
- /**
- * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
- */
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(
- PrincipalCollection principals) {
- ShiroUser shiroUser = (ShiroUser) principals.fromRealm(getName())
- .iterator().next();
- User user = accountManager
- .findUserByLoginName(shiroUser.getLoginName());
- if (user != null) {
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- info.addStringPermissions(accountManager.getUserPermissions(user.getId()));
- return info;
- } else {
- return null;
- }
- }
- /**
- * 更新用户授权信息缓存.
- */
- public void clearCachedAuthorizationInfo(String principal) {
- SimplePrincipalCollection principals = new SimplePrincipalCollection(
- principal, getName());
- clearCachedAuthorizationInfo(principals);
- }
- /**
- * 清除所有用户授权信息缓存.
- */
- public void clearAllCachedAuthorizationInfo() {
- Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
- if (cache != null) {
- for (Object key : cache.keys()) {
- cache.remove(key);
- }
- }
- }
- @Autowired
- public void setAccountManager(AccountManager accountManager) {
- this.accountManager = accountManager;
- }
- /**
- * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息.
- */
- public static class ShiroUser implements Serializable {
- private static final long serialVersionUID = -1748602382963711884L;
- private String loginName;
- private String name;
- public ShiroUser(String loginName, String name) {
- this.loginName = loginName;
- this.name = name;
- }
- public String getLoginName() {
- return loginName;
- }
- /**
- * 本函数输出将作为默认的<shiro:principal/>输出.
- */
- @Override
- public String toString() {
- return loginName;
- }
- public String getName() {
- return name;
- }
- }
- }