使用Mongodb+Shiro+SpringMVC实现动态权限分配

时间:2023-03-08 21:23:02

此次的文档只对Mongodb整合Shiro并且实现动态权限分配做整理,其它的内容以后会补上。

第一步、创建在web.xml中配置 Spring 、Shiro

shiroFilter 过滤器是用来将请求交给shiro来管理

 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list> <!-- 加载spring的配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-config.xml,classpath:dispatcher-shiro.xml</param-value>
</context-param>
<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>
<filter>
<filter-name>XSSCheckFilter</filter-name>
<filter-class>com.java.filter.XSSCheckFilter</filter-class>
<init-param>
<param-name>errorPath</param-name>
<param-value>/fail/error</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>XSSCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.java.listener.Log4jListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher-servlet.xml,classpath:dispatcher-shiro.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<error-page>
<error-code>404</error-code>
<location>/fail/nopage</location>
</error-page>
<error-page>
<exception-type>org.apache.shiro.authz.UnauthorizedException</exception-type>
<location>/fail/nopromission</location>
</error-page>
<error-page>
<exception-type>org.apache.shiro.authz.AuthorizationException</exception-type>
<location>/fail/nopromission</location>
</error-page>
</web-app>

第二步、创建application-config.xml 用来配置Mongodb链接数据库 并且注入mongodbTemplate

 <?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:tx="http://www.springframework.org/schema/tx" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
<!-- 自动扫包 -->
<context:component-scan base-package="com.java.*">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan> <mongo:mongo host="127.0.0.1" port="27017">
<mongo:options connections-per-host="10"
threads-allowed-to-block-for-connection-multiplier="5"
connect-timeout="10000" max-wait-time="120000" auto-connect-retry="true"
socket-keep-alive="false" socket-timeout="0" slave-ok="false"
write-number="1" write-timeout="0" write-fsync="true" />
</mongo:mongo> <mongo:db-factory dbname="webFramework" mongo-ref="mongo" />
<!-- <mongo:db-factory dbname="webFrameworktest" mongo-ref="mongo" /> --> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean> </beans>

第三步、创建dispatch-servlet.xml 配置文件, 配置springMVC的相关配置

 <?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- 加载Properties文件 -->
<!-- <bean id="configurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"> <list> <value>classpath:email.properties</value>
</list> </property> </bean> --> <mvc:view-controller path="/" view-name="redirect:user/login" /> <aop:aspectj-autoproxy />
<!-- 注解探测器(组件扫描)@Controller -->
<context:component-scan base-package="com.java.*">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository" />
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Service" />
</context:component-scan> <!-- 启动springMVC的注解功能,他会自动注册HandlerMapping,HandlerAdapter,ExceptionResolver的相关实例 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
</bean>
<ref bean="mappingJacksonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
<!-- enable autowire --> <mvc:view-controller path="/" view-name="redirect:user/login"/><!--设置默认的主页 --> <!-- 处理JSON数据转换的 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<!-- 为了处理返回的JSON数据的编码,默认是ISO-88859-1的,这里把它设置为UTF-8,解决有乱码的情况 -->
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean> <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP -->
<!-- 默认的视图解析器在上边的解析错误时使用 (默认使用html)- --> <bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/" p:suffix=".jsp" /> <!--由于web.xml中设置是由springMVC拦截所有请求,于是在读取静态资源文件的时候就会受到影响(说白了就是读不到) 经过下面的配置,该标签的作用就是所有页面中引用/css/**的资源,都会从/resources/styles/
里面查找 -->
<mvc:resources mapping="/logs/**" location="/logs/" />
<mvc:resources mapping="/file/**" location="/file/" />
<mvc:resources mapping="/assets/**" location="/WEB-INF/assets/" />
<mvc:resources mapping="/FileUpload/**" location="/WEB-INF/FileUpload/" /> <!--由于web.xml中设置是由springMVC拦截所有请求,于是在读取静态资源文件的时候就会受到影响(说白了就是读不到) 经过下面的配置,该标签的作用就是所有页面中引用/css/**的资源,都会从/resources/styles/
里面查找 -->
<!-- <mvc:resources mapping="/logs/**" location="/logs/" /> <mvc:resources
mapping="/file/**" location="/file/" /> <mvc:resources mapping="/assets/**"
location="/WEB-INF/assets/"/> -->
<!-- 拦截器 --> <!-- 多个拦截器,顺序执行 -->
<!-- 如果不配置或/*,将拦截所有的Controller -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*" />
<mvc:mapping path="/*/*" />
<mvc:exclude-mapping path="/user/login" />
<mvc:exclude-mapping path="/user/loginToIndex" />
<mvc:exclude-mapping path="/init/toinitPage" />
<mvc:exclude-mapping path="/init/toInit" />
<bean class="com.java.interceptor.LoginInterceptor">
</bean>
</mvc:interceptor>
</mvc:interceptors> <!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- set the max upload size 100M -->
<property name="maxUploadSize">
<value>104857600</value>
</property>
<property name="maxInMemorySize">
<value>1024000</value>
</property>
</bean> </beans>

第四步、配置dispatch-shiro.xml配置文件,用来配置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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- shiro-core 核心包中 MDB加密算法,需要对密码进行加密处理 -->
<!-- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher"></bean> -->
<!-- 缓存管理 -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean> <bean id="mongodbRealm" class="com.java.shiro.MongoDBRealm">
<!-- 设置加密模式 -->
<!-- <property name="credentialsMatcher" ref="credentialsMatcher"></property> -->
</bean> <!-- shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="mongodbRealm"></property>
<property name="cacheManager" ref="cacheManager"></property>
</bean> <bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/user/login" />
<property name="successUrl" value="/user/index" />
<property name="unauthorizedUrl" value="/fail/nopromission" />
<!-- 自定义权限配置 -->
<property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" />
</bean> <!-- 自定义filterChainDefinitionMap -->
<bean id="chainDefinitionSectionMetaSource" class="com.java.shiro.ChainDefinitionSectionMetaSource">
<property name="filterChainDefinitions">
<value>
/user/loginToIndex = anon
/assets/** = anon
/fail/nopromission = anon
/fail/nopage = anon
/fail/error = anon
/public/** = anon
/FileUpload/** = anon
/init/toinitPage = anon
/init/toInit = anon
</value>
</property>
</bean> <!-- <bean id="shiroFilter" class="com.java.shiro.ShiroPermissionFactory">
调用我们配置的权限管理器
<property name="securityManager" ref="securityManager" />
配置我们的登录请求地址
<property name="loginUrl" value="/admin/tologin" />
配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址
<property name="successUrl" value="/admin/loginToIndex" />
如果您请求的资源不再您的权限范围,则跳转到/403请求地址
<property name="unauthorizedUrl" value="/fail/nopromission" />
权限配置
<property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource"></property>
<property name="filterChainDefinitions">
<value>
/admin/tologin = anon
/assets/** =anon
/fail/nopromission =anon
</value>
</property>
</bean>
--> </beans>

第五步、自定义filterChainDefinitionMap ,创建ChainDefinitionSectionMetaSource

 package com.java.shiro;

 import java.util.List;
import java.util.Map; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.swing.plaf.synth.SynthColorChooserUI; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.jfree.util.Log;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils; import com.java.manage.dao.ResourceDao;
import com.java.manage.pojo.Resource; public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section> { private String filterChainDefinitions; public Ini.Section getObject() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext(new String[] { "application-config.xml" });
ResourceDao resourceDao = (ResourceDao) ac.getBean("resourceDaoImpl");
// 获取所有Resource
Ini ini = new Ini();
// 加载默认的url
ini.load(filterChainDefinitions);
Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
List<Resource> list = resourceDao.find(new Query());
// 循环Resource的url,逐个添加到section中。section就是filterChainDefinitionMap,
// 里面的键就是链接URL,值就是存在什么条件才能访问该链接
for (Resource resource : list) {
// 构成permission字符串
if (StringUtils.isNotEmpty(resource.getResUrl() + "")
&& StringUtils.isNotEmpty(resource.getResKey() + "")) {
String permission = "perms[" + resource.getResKey() + "]";
Log.info("所有权限信息" + permission);
// 不对角色进行权限验证
// 如需要则 permission = "roles[" + resources.getResKey() + "]";
// map.put(resource.getResUrl() + "", permission);
section.put(resource.getResUrl() + "", permission);
}
}
/*
* for (Map.Entry<String, String> entry :map.entrySet()) {
*
* System.out.println("Key = " + entry.getKey() + ", Value = " +
* entry.getValue());
*
* }
*/ // 所有资源的访问权限,必须放在最后
/* section.put("/**", "authc"); */
/**
* 如果需要一个用户只能登录一处地方,,修改为 section.put("/**",
* "authc,kickout,sysUser,user");
**/
section.put("/**", "authc");
return section;
} /**
* 通过filterChainDefinitions对默认的url过滤定义
*
* @param filterChainDefinitions
* 默认的url过滤定义
*/
public void setFilterChainDefinitions(String filterChainDefinitions) {
this.filterChainDefinitions = filterChainDefinitions;
} public Class<?> getObjectType() {
return this.getClass();
} public boolean isSingleton() {
return false;
}
}

第六步、自定义MongoDBRealm

 package com.java.shiro;

 import java.util.List;

 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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import com.java.manage.pojo.Resource;
import com.java.manage.pojo.User;
import com.java.manage.service.ResourceService;
import com.java.manage.service.UserService;
import com.java.util.Common;
import com.java.util.Constant; public class MongoDBRealm extends AuthorizingRealm {
private static final Logger log = LoggerFactory.getLogger(MongoDBRealm.class);
@Autowired
private UserService userServiceImpl;
@Autowired
private ResourceService resourceService; /*
* @Autowired private ResUserService resUserService;
*/ @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取登录的用户名
String accountName = SecurityUtils.getSubject().getPrincipal().toString();
// 获取shirosession
log.info("当前登录用户:" + accountName);
if (accountName != null || accountName != "") {
Session session = SecurityUtils.getSubject().getSession();
try {
User user = (User) session.getAttribute(Constant.USER_SESSION);
if (user != null) { // 通过用户名获取用户对象
User u = this.userServiceImpl.findUserById(user.getId());
List<Resource> rs = u.getResource();
// 权限信息对象info,用来存放查出的用户所有角色(role)及权限(permission)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (Resource r : rs) {
log.info("资源:" + r.getName() + ":" + r.getResUrl());
info.addStringPermission(r.getResKey());
}
session.setAttribute("resourceslist", rs);
log.info("当前登录用户访问资源权限:" + info);
return info;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户的名称
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
try { // 根据获取到的用户信息从数据库查询是否存在该用户名下的信息
User user = this.userServiceImpl.findUserByAccountName(usertoken.getUsername()); if (user != null) {
// 当验证都通过后,把用户信息放在session里
Session session = SecurityUtils.getSubject().getSession();
// List<ResUser> ru=
// this.resUserService.findResourcesIdByUserId(user.getId());
User u = this.userServiceImpl.findUserById(user.getId()); // 通过集合获取资源
// List<Resource> rs=this.resourceService.findlistResource(ru);
List<Resource> rs = u.getResource();
session.setAttribute(Constant.USER_SESSION, user);
session.setAttribute("userSessionId", user.getId());
session.setAttribute("resourceslist", rs);
return new SimpleAuthenticationInfo(user.getAccountName(), user.getPassWord(), Constant.REALMNAME);
} else {
return null;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null;
} }

欢迎大家加入java资源免费分享群,群号:814657026

<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=0d0276c9d13e09101c2aa111b6cc2b5e43f1465cbe431c198a02cc729288094f"><img border="0" src="//pub.idqqimg.com/wpa/images/group.png" alt="MQ" title="MQ"></a>