spring+springmvc+mybatis+shiro+ehcache集成demo

实际使用shiro的时候大部分都是和spring等框架结合使用,主要就是配置web.xml将shiro的filter和spring容器bean的filter关联起来,生命周期由servlet容器来控制,然后配置shiro的spring的xml文件,其中主要配置shiro过滤器securityManager、认证成功失败的跳转页面、过滤链、凭证匹配器(hash 还是 sha1等)、自定义的realm、缓存管理器(ehcache、redis)、会话管理器等等

一般权限表设计都是5张:

用户表

spring+springmvc+mybatis+shiro+ehcache集成demo_第1张图片

这里写图片描述

角色表

spring+springmvc+mybatis+shiro+ehcache集成demo_第2张图片

这里写图片描述

权限表

spring+springmvc+mybatis+shiro+ehcache集成demo_第3张图片

这里写图片描述

用户角色表

spring+springmvc+mybatis+shiro+ehcache集成demo_第4张图片

这里写图片描述

角色权限表

spring+springmvc+mybatis+shiro+ehcache集成demo_第5张图片

这里写图片描述

用户表 -n:n- 角色表 中间表是 用户角色表
角色表 -n:n- 权限表 中间表示 角色权限表

下面是demo工程的结构:

spring+springmvc+mybatis+shiro+ehcache集成demo_第6张图片

主要贴出下面几个配置文件:

springmvc.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.lijie.controller"/>
    <mvc:annotation-driven/>
    
    <aop:config proxy-target-class="true">aop:config>
beans>

applicationContext-shiro.xml

<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

    
    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        
        <property name="loginUrl" value="/login"/>
        
        <property name="successUrl" value="/success"/>
        
        <property name="unauthorizedUrl" value="/authorfail"/>
        
        <property name="filterChainDefinitions">
            <value>
                /logout = logout
                /** = authc
            value>
        property>
    bean>

    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
        
        <property name="cacheManager" ref="cacheManager"/>
        
        <property name="sessionManager" ref="sessionManager"/>
    bean>

    
    <bean id="myRealm" class="com.lijie.shiro.MyRealm">
        
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    bean>

    
    <bean id="credentialsMatcher"
          class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="1"/>
    bean>

    
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:spring/shiro-ehcache.xml"/>
    bean>

    
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        
        <property name="globalSessionTimeout" value="600000"/>
        
        <property name="deleteInvalidSessions" value="true"/>
    bean>

    
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    bean>

beans>

其中上面的过滤链有很多种拦截:

anon
org.apache.shiro.web.filter.authc.AnonymousFilter
authc
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port
org.apache.shiro.web.filter.authz.PortFilter
rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl
org.apache.shiro.web.filter.authz.SslFilter
user
org.apache.shiro.web.filter.authc.UserFilter
logout
org.apache.shiro.web.filter.authc.LogoutFilter

例子:
1.anon:  例子/lijie/**=anon 没有参数,表示可以匿名使用。
2.authc: 例如/lijie/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数 
3.perms:例子/lijie/user/**=perms[test:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["test:add:*,test:delete:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
4.user:  例子/lijie/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

applicationContext-service.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       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-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    
    <context:component-scan base-package="com.lijie.service,com.lijie.shiro">context:component-scan>
beans>

applicationContext-dao.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    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-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    
    
    <context:property-placeholder location="classpath:resource/*.properties" />
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="maxActive" value="10" />
        <property name="minIdle" value="5" />
    bean>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        
        <property name="dataSource" ref="dataSource" />
        
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        
        <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
    bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lijie.mapper" />
    bean>
beans>

applicationContext-trans.xml 切面事务配置

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    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-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
    
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        
        <property name="dataSource" ref="dataSource" />
    bean>
    
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="create*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
        tx:attributes>
    tx:advice>
    
    <aop:config>
        <aop:advisor advice-ref="txAdvice"
            pointcut="execution(* com.lijie.service.*.*(..))" />
    aop:config>
beans>

shiro-ehcache.xml 缓存配置

<ehcache>
    
    <diskStore path="./ehcache" />
    <defaultCache 
        maxElementsInMemory="1000" 
        maxElementsOnDisk="10000000"
        eternal="false" 
        overflowToDisk="false" 
        diskPersistent="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120" 
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    defaultCache>
ehcache>

SqlMapConfig.xml mybatis的插件配置



<configuration>
    
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            
            <property name="dialect" value="mysql"/>
        plugin>
    plugins>
configuration>

web.xml


<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_3_0.xsd"
         version="3.0">
    <display-name>myshirodisplay-name>

    
    <context-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath:spring/applicationContext-*.xmlparam-value>
    context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
    listener>

    
    <filter>
        <filter-name>CharacterEncodingFilterfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

    
    <servlet>
        <servlet-name>web-demoservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:spring/springmvc.xmlparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>web-demoservlet-name>
        
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <filter>
        <filter-name>shiroFilterfilter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
        
        <init-param>
            <param-name>targetFilterLifecycleparam-name>
            <param-value>trueparam-value>
        init-param>
        
        <init-param>
            <param-name>targetBeanNameparam-name>
            <param-value>shiroFilterparam-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>shiroFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
web-app>

自定义的Realm MyRealm

public class MyRealm extends AuthorizingRealm {

    protected static final Logger logger = LoggerFactory.getLogger(MyRealm.class);

    @Autowired
    private SysService sysService;

    /**
     * 认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        // 获取凭证账号
        String username = (String) authenticationToken.getPrincipal();

        logger.info("AuthenticationInfo 开始认证-》:{}", username);

        SysUser sysUserByUserCode = null;
        List permissionListByUserId = null;
        try {
            //通过账号查询用户信息
            sysUserByUserCode = sysService.findSysUserByUserCode(username);

            //通过账号查询用户菜单
            permissionListByUserId = sysService.findMenuListByUserId(username);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 账号为空返回null
        if (null == sysUserByUserCode) {
            return null;
        }

        // 相当于session里面存储的
        ActiveUser user = new ActiveUser();
        user.setUserid(sysUserByUserCode.getId());
        user.setUsercode(sysUserByUserCode.getUsercode());
        user.setUsername(sysUserByUserCode.getUsername());
        user.setMenus(permissionListByUserId);

        // 组装的info
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                user, sysUserByUserCode.getPassword(), ByteSource.Util.bytes(sysUserByUserCode.getSalt()), this.getClass().getName());

        return simpleAuthenticationInfo;
    }

    /**
     * 授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        // 取出身份信息
        ActiveUser user = (ActiveUser) principalCollection.getPrimaryPrincipal();

        logger.info("AuthorizationInfo 开始授权-》:{}", user.getUserid());

        List permissionListByUserId = null;

        // 查询具体权限
        try {
            permissionListByUserId = sysService.findPermissionListByUserId(user.getUserid());
        } catch (Exception e) {
            e.printStackTrace();
        }

        List permissions = null;

        if (!CollectionUtils.isEmpty(permissionListByUserId)) {
            permissions = new ArrayList<>();
            for (SysPermission perm : permissionListByUserId) {
                permissions.add(perm.getPercode());
            }
        }

        //查到权限数据,返回授权信息(要包括 上边的permissions)
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //将上边查询到授权信息填充到simpleAuthorizationInfo对象中
        simpleAuthorizationInfo.addStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }

    //清除缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }
}

以及一个测试的controller:

@Controller
public class TestController {

    protected static final Logger logger = LoggerFactory.getLogger(TestController.class);

    @Autowired
    private TestService testService;
    @Autowired
    private SysService sysService;

    @RequestMapping("/user/{name}/desc/{desc}")
    @RequiresPermissions(value = {"test:query", "test:xxx"}, logical = Logical.OR)
    @ResponseBody
    public Response getResponse(@PathVariable String name, @PathVariable String desc) {
        return Response.success(testService.getTestVo(name, desc));
    }

    @RequestMapping("/login")
    @ResponseBody
    public Response login(HttpServletRequest request) {

        Subject subject = SecurityUtils.getSubject();

        //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
        String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
        String url = request.getRequestURI();
        //根据shiro返回的异常类路径判断,抛出指定异常信息
        if (exceptionClassName != null) {
            if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
                return Response.authenfail();
            } else if (IncorrectCredentialsException.class.getName().equals(
                    exceptionClassName)) {
                return Response.authenfail();
            } else {
                return Response.authenfail();
            }
        }

        if (subject.isAuthenticated()) {
            return Response.success("已经登录");
        } else {
            return Response.success("请先登录");
        }

    }

    @RequestMapping("/authenfail")
    @ResponseBody
    public Response authenfail() {
        return Response.authenfail();
    }

    @RequestMapping("/authorfail")
    @ResponseBody
    public Response authorfail() {
        return Response.authorfail();
    }

    @RequestMapping("/success")
    @ResponseBody
    public Response success() {
        return Response.success("登录成功");
    }
}

因为没有页面,所以全都是ResponseBody,可以使用postman测试,启动程序,然后先进行登录操作:

spring+springmvc+mybatis+shiro+ehcache集成demo_第7张图片

然后再进行getResponse这个controller的访问:

spring+springmvc+mybatis+shiro+ehcache集成demo_第8张图片

你可能感兴趣的:(Java,shiro)