图片 4

SpringBoot整合mybatis、shiro、redis达成基于数据库的细粒度动态权限管理种类实例

在项目中常常需要后台程序的持久层查询数据库来获取数据,然后将数据交给服务层、控制层,最后才交给视图层。如果数据访问缓慢,就会影响程序的运行。

1.前言

本文主要介绍使用SpringBoot与shiro实现基于数据库的细粒度动态权限管理系统实例。
使用技术:SpringBoot、mybatis、shiro、thymeleaf、pagehelper、Mapper插件、druid、dataTables、ztree、jQuery
开发工具:intellij idea
数据库:mysql、redis
基本上是基于使用SpringSecurity的demo上修改而成,地址

在《Redis之——Spring整合Redis》一文中,向大家介绍了如何将spring与Redis整合起来,但不是基于注解的形式,很多同学都希望能够通过注解的形式来简单的将Spring与Redis整合起来,这样,在使用的时候,只需要在相应的方法上加上注解,便可以使方法轻松的调用Redis的缓存。那么今天就来向大家介绍如何用基于注解的形式来整合Spring与Redis。

为了加快程序的运行,可以将数据放入缓存中,包括数据缓存和页面缓存。

2.表结构

还是是用标准的5张表来展现权限。如下图:图片 1
分别为用户表,角色表,资源表,用户角色表,角色资源表。在这个demo中使用了mybatis-generator自动生成代码。运行mybatis-generator:generate
-e 根据数据库中的表,生成
相应的model,mapper单表的增删改查。不过如果是导入本项目的就别运行这个命令了。新增表的话,也要修改mybatis-generator-config.xml中的tableName,指定表名再运行。

一、项目搭建

所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例。这样做可以减少系统开销,提高系统效率。

3.maven配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.study</groupId>
    <artifactId>springboot-shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot-shiro</name>
    <description>Demo project for Spring Boot</description>

    <parent>        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-thymeleaf</artifactId>        </dependency>        <dependency>            <groupId>com.github.pagehelper</groupId>            <artifactId>pagehelper-spring-boot-starter</artifactId>            <version>1.1.0</version>        </dependency>        <dependency>            <groupId>tk.mybatis</groupId>            <artifactId>mapper-spring-boot-starter</artifactId>            <version>1.1.1</version>        </dependency>        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-spring</artifactId>            <version>1.3.2</version>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid</artifactId>            <version>1.0.29</version>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>        </dependency>        <dependency>            <groupId>net.sourceforge.nekohtml</groupId>            <artifactId>nekohtml</artifactId>            <version>1.9.22</version>        </dependency>        <dependency>            <groupId>com.github.theborakompanioni</groupId>            <artifactId>thymeleaf-extras-shiro</artifactId>            <version>1.2.1</version>        </dependency>        <dependency>            <groupId>org.crazycake</groupId>            <artifactId>shiro-redis</artifactId>            <version>2.4.2.1-RELEASE</version>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>            <plugin>                <groupId>org.mybatis.generator</groupId>                <artifactId>mybatis-generator-maven-plugin</artifactId>                <version>1.3.5</version>                <configuration>                    <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>                    <overwrite>true</overwrite>                    <verbose>true</verbose>                </configuration>                <dependencies>                    <dependency>                        <groupId>mysql</groupId>                        <artifactId>mysql-connector-java</artifactId>                        <version>${mysql.version}</version>                    </dependency>                    <dependency>                        <groupId>tk.mybatis</groupId>                        <artifactId>mapper</artifactId>                        <version>3.4.0</version>                    </dependency>                </dependencies>            </plugin>        </plugins>    </build></project>

今天,我们不使用hibernate来操作数据库了,我们今天选择的框架是:
Spring4(包括mvc、context、orm) +
MyBatis3,所以,我们今天的基础架构是:Spring4(包括mvc、context、orm) +
MyBatis3+Redis

其中页面缓存主要是oscache,可以整页或者指定网页某一部分缓存,同时指定他的过期时间,这样在此时间段里面访问的数据都是一样的

4.配置Druid

package com.study.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * Created by yangqj on 2017/4/19.
 */
@Configuration
public class DruidConfig {
    @Bean
    public ServletRegistrationBean druidServlet() {

        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        //登录查看信息的账号密码.

        servletRegistrationBean.addInitParameter("loginUsername","admin");
        servletRegistrationBean.addInitParameter("loginPassword","123456");
        return servletRegistrationBean;
    }
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter;
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

在application.properties中加入:

# 数据源基础配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro
spring.datasource.username=root
spring.datasource.password=root
# 连接池配置
# 初始化大小,最小,最大
spring.datasource.initialSize=1
spring.datasource.minIdle=1
spring.datasource.maxActive=20

配置好后,运行项目访问
输入配置的账号密码admin,123456进入:图片 2

1、构建pom.xml

数据缓存则比较多,有著名的ehcache,redis,memcached等。

5.配置mybatis

使用springboot 整合mybatis非常方便,只需在application.properties

mybatis.type-aliases-package=com.study.model
mybatis.mapper-locations=classpath:mapper/*.xml
mapper.mappers=com.study.util.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true

pagehelper.params=count\=countSql

将相应的路径改成项目包所在的路径即可。配置文件中可以看出来还加入了pagehelper
和Mapper插件。如果不需要,把上面配置文件中的 pagehelper删除。

<project 
  xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
  <modelVersion>4.0.0</modelVersion> 
  <groupId>lyz</groupId> 
  <artifactId>lyz</artifactId> 
  <version>1.0.0</version> 
  <packaging>war</packaging> 
  <name>lyz</name> 
  <description>Basic Data Platform</description> 

  <properties> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    <spring.version>4.2.0.RELEASE</spring.version> 
  </properties> 

  <dependencies> 

    <!-- spring mvc related.....start --> <!-- TODO: replace jackson with fastjson --> 
    <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-context</artifactId> 
      <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-aop</artifactId> 
      <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-webmvc</artifactId> 
      <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-web</artifactId> 
      <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>javax.servlet</groupId> 
      <artifactId>jstl</artifactId> 
      <version>1.2</version> 
    </dependency> 
    <dependency> 
      <groupId>commons-logging</groupId> 
      <artifactId>commons-logging</artifactId> 
      <version>1.1.3</version> 
    </dependency> 
    <dependency> 
      <groupId>org.codehaus.jackson</groupId> 
      <artifactId>jackson-mapper-asl</artifactId> 
      <version>1.9.13</version> 
    </dependency> 
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId> 
      <artifactId>jackson-annotations</artifactId> 
      <version>2.6.1</version> 
    </dependency> 
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId> 
      <artifactId>jackson-core</artifactId> 
      <version>2.6.1</version> 
    </dependency> 
    <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId> 
      <artifactId>jackson-databind</artifactId> 
      <version>2.6.1</version> 
    </dependency> 
    <!-- spring mvc related.....end --> 

    <!-- mybatis orm related.....start --> 
    <dependency> 
      <groupId>org.springframework</groupId> 
      <artifactId>spring-orm</artifactId> 
      <version>${spring.version}</version> 
    </dependency> 
    <dependency> 
      <groupId>org.mybatis</groupId> 
      <artifactId>mybatis-spring</artifactId> 
      <version>1.2.3</version> 
    </dependency> 
    <dependency> 
      <groupId>mysql</groupId> 
      <artifactId>mysql-connector-java</artifactId> 
      <version>5.1.36</version> 
    </dependency> 
    <dependency> 
      <groupId>org.mybatis</groupId> 
      <artifactId>mybatis</artifactId> 
      <version>3.3.0</version> 
    </dependency> 
    <dependency> 
      <groupId>c3p0</groupId> 
      <artifactId>c3p0</artifactId> 
      <version>0.9.1.2</version> 
    </dependency> 
    <dependency> 
      <groupId>org.slf4j</groupId> 
      <artifactId>slf4j-log4j12</artifactId> 
      <version>1.7.12</version> 
    </dependency> 
    <!-- mybatis orm related.....end --> 

    <!-- project log related.....start --> 
    <dependency> 
      <groupId>log4j</groupId> 
      <artifactId>log4j</artifactId> 
      <version>1.2.17</version> 
    </dependency> 
    <!-- project log related.....end --> 

    <!-- redis cache related.....start --> 
    <dependency> 
      <groupId>org.springframework.data</groupId> 
      <artifactId>spring-data-redis</artifactId> 
      <version>1.6.0.RELEASE</version> 
    </dependency> 
    <dependency> 
      <groupId>redis.clients</groupId> 
      <artifactId>jedis</artifactId> 
      <version>2.7.3</version> 
    </dependency> 
    <!-- redis cache related.....end --> 
  </dependencies> 

  <build> 
    <plugins> 
      <plugin> 
        <artifactId>maven-compiler-plugin</artifactId> 
        <version>3.1</version> 
        <configuration> 
          <source>1.7</source> 
          <target>1.7</target> 
        </configuration> 
      </plugin> 
      <plugin> 
        <artifactId>maven-war-plugin</artifactId> 
        <version>2.4</version> 
        <configuration> 
          <warSourceDirectory>WebContent</warSourceDirectory> 
          <failOnMissingWebXml>false</failOnMissingWebXml> 
        </configuration> 
      </plugin> 
    </plugins> 
  </build> 
</project> 

本文主要简单的使用Java代码进行redis缓存,即在查询的时候先在service层从redis缓存中获取数据。如果不存在,则再经过dao层从数据库中获取,最后将查询到的数据缓存到redis中;如果存在,直接从redis缓存中读取,并交给controller层。

MyMapper:
package com.study.util;

/**
 * Created by yangqj on 2017/4/20.
 */
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
}

对于Springboot整合mybatis可以参考

2、创建测试数据库:lyz,测试数据表:user

xml配置:

6.thymeleaf配置

thymeleaf是springboot官方推荐的,所以来试一下。
首先加入配置:

#spring.thymeleaf.prefix=classpath:/templates/
#spring.thymeleaf.suffix=.html
#spring.thymeleaf.mode=HTML5
#spring.thymeleaf.encoding=UTF-8
# ;charset=<encoding> is added
#spring.thymeleaf.content-type=text/html
# set to false for hot refresh
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5

可以看到其实上面都是注释了的,因为springboot会根据约定俗成的方式帮我们配置好。所以上面注释部分是springboot自动配置的,如果需要自定义配置,只需要修改上注释部分即可。
后两行没有注释的部分,spring.thymeleaf.cache=false表示关闭缓存,这样修改文件后不需要重新启动,缓存默认是开启的,所以指定为false。但是在intellij
idea中还需要按Ctrl + Shift + F9.
对于spring.thymeleaf.mode=LEGACYHTML5。thymeleaf对html中的语法要求非常严格,像我从网上找的模板,使用thymeleaf后报一堆的语法错误,后来没办法,使用弱语法校验,所以加入配置spring.thymeleaf.mode=LEGACYHTML5。加入这个配置后还需要在maven中加入

<dependency>
    <groupId>net.sourceforge.nekohtml</groupId>
    <artifactId>nekohtml</artifactId>
    <version>1.9.22</version>
</dependency>

否则会报错的。
在前端页面的头部加入一下配置后,就可以使用thymeleaf了

<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />

不过这个项目因为使用了datatables都是使用jquery
的ajax来访问数据与处理数据,所以用到的thymeleaf语法非常少,基本上可以参考的就是js即css的导入和类似于jsp的include功能的部分页面引入。
对于静态文件的引入:

 <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" />

而文件在项目中的位置是static-css-bootstrap.min.css。为什么这样可以访问到该文件,也是因为springboot对于静态文件会自动查找/static
public、/resources、/META-INF/resources下的文件。所以不需要加static.

页面引入:
局部页面如下:

<div  th:fragment="top">    ...</div>

主体页面映入方式:

<div th:include="common/top :: top"></div>

inclide=”文件路径::局部代码片段名称”

DROP DATABASE IF EXISTS `lyz`; 
CREATE DATABASE `lyz`; 
DROP TABLE IF EXISTS `user`; 
CREATE TABLE `user` ( 
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
 `name` varchar(255) DEFAULT NULL, 
 `sex` varchar(255) DEFAULT NULL, 
 `age` int(11) DEFAULT NULL, 
 PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
bean  property name="maxTotal" value="${redis.maxTotal}"/property property name="maxIdle" value="${redis.maxIdle}"/property property name="testOnBorrow" value="${redis.testOnBorrow}"/property property name="maxWaitMillis" value="${redis.maxWaitMillis}"/property /bean !-- jedis客户端单机版 -- bean  constructor-arg name="poolConfig" ref="poolConfig"/constructor-arg constructor-arg name="host" value="${redis.hostName}"/constructor-arg constructor-arg name="port" value="${redis.port}"/constructor-arg /bean !-- jedis集群版配置 -- bean  constructor-arg name="poolConfig" ref="poolConfig"/constructor-arg constructor-arg name="nodes" set bean  constructor-arg name="host" value="127.0.0.1"/constructor-arg constructor-arg name="port" value="7001"/constructor-arg /bean bean  constructor-arg name="host" value="127.0.0.1"/constructor-arg constructor-arg name="port" value="7002"/constructor-arg /bean bean  constructor-arg name="host" value="127.0.0.1"/constructor-arg constructor-arg name="port" value="7003"/constructor-arg /bean bean  constructor-arg name="host" value="127.0.0.1"/constructor-arg constructor-arg name="port" value="7004"/constructor-arg /bean bean  constructor-arg name="host" value="127.0.0.1"/constructor-arg constructor-arg name="port" value="7005"/constructor-arg /bean /set /constructor-arg /bean [java] view plain copyjava调用 //service层查询时代码 @Autowired private JedisClient jedisClient; @Override public ListUser list() { // TODO Auto-generated method stub try { String json = jedisClient.hget("user", "list"); if (StringUtils.isNotBlank(json)) { ListUser users = JsonUtils.jsonToList(json, User.class); return users; } } catch (Exception e) { e.printStackTrace(); } //使用持久层查询数据库 ListUser list = userMapper.list(); try { jedisClient.hset("user", "list", JsonUtils.objectToJson(list)); } catch (Exception e) { e.printStackTrace(); } return list; } 

7.shiro配置

3、创建实体类user

总结

配置文件ShiroConfig
package com.study.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.github.pagehelper.util.StringUtil;
import com.study.model.Resources;
import com.study.service.ResourcesService;
import com.study.shiro.MyShiroRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by yangqj on 2017/4/23.
 */
@Configuration
public class ShiroConfig {
    @Autowired(required = false)
    private ResourcesService resourcesService;

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.timeout}")    private int timeout;    @Bean    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {        return new LifecycleBeanPostProcessor();    }    /**     * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean     * @return     */    @Bean    public ShiroDialect shiroDialect() {        return new ShiroDialect();    }    /**     * ShiroFilterFactoryBean 处理拦截资源文件问题。     * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager     *     Filter Chain定义说明     1、一个URL可以配置多个Filter,使用逗号分隔     2、当设置多个过滤器时,全部验证通过,才视为通过     3、部分过滤器可指定参数,如perms,roles     *     */    @Bean    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){        System.out.println("ShiroConfiguration.shirFilter;        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();        // 必须设置 SecurityManager        shiroFilterFactoryBean.setSecurityManager(securityManager);        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面        shiroFilterFactoryBean.setLoginUrl;        // 登录成功后要跳转的链接        shiroFilterFactoryBean.setSuccessUrl("/usersPage");        //未授权界面;        shiroFilterFactoryBean.setUnauthorizedUrl;        //拦截器.        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了        filterChainDefinitionMap.put("/logout", "logout");        filterChainDefinitionMap.put("/css/**","anon");        filterChainDefinitionMap.put("/js/**","anon");        filterChainDefinitionMap.put("/img/**","anon");        filterChainDefinitionMap.put("/font-awesome/**","anon");        //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->        //自定义加载权限资源关系        List<Resources> resourcesList = resourcesService.queryAll();         for(Resources resources:resourcesList){            if (StringUtil.isNotEmpty(resources.getResurl {                String permission = "perms[" + resources.getResurl()+ "]";                filterChainDefinitionMap.put(resources.getResurl(),permission);            }        }        filterChainDefinitionMap.put("/**", "authc");        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);        return shiroFilterFactoryBean;    }    @Bean    public SecurityManager securityManager(){        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();        //设置realm.        securityManager.setRealm(myShiroRealm;        // 自定义缓存实现 使用redis        //securityManager.setCacheManager(cacheManager;        // 自定义session管理 使用redis        securityManager.setSessionManager(sessionManager;        return securityManager;    }    @Bean    public MyShiroRealm myShiroRealm(){        MyShiroRealm myShiroRealm = new MyShiroRealm();        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher;        return myShiroRealm;    }    /**     * 凭证匹配器     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了     *  所以我们需要修改下doGetAuthenticationInfo中的代码;     * )     * @return     */    @Bean    public HashedCredentialsMatcher hashedCredentialsMatcher(){        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();        hashedCredentialsMatcher.setHashAlgorithmName;//散列算法:这里使用MD5算法;        hashedCredentialsMatcher.setHashIterations;//散列的次数,比如散列两次,相当于 md5;        return hashedCredentialsMatcher;    }    /**     *  开启shiro aop注解支持.     *  使用代理方式;所以需要开启代码支持;     * @param securityManager     * @return     */    @Bean    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);        return authorizationAttributeSourceAdvisor;    }    /**     * 配置shiro redisManager     * 使用的是shiro-redis开源插件     * @return     */    public RedisManager redisManager() {        RedisManager redisManager = new RedisManager();        redisManager.setHost;        redisManager.setPort;        redisManager.setExpire;// 配置缓存过期时间        redisManager.setTimeout;        // redisManager.setPassword;        return redisManager;    }    /**     * cacheManager 缓存 redis实现     * 使用的是shiro-redis开源插件     * @return     */    public RedisCacheManager cacheManager() {        RedisCacheManager redisCacheManager = new RedisCacheManager();        redisCacheManager.setRedisManager(redisManager;        return redisCacheManager;    }    /**     * RedisSessionDAO shiro sessionDao层的实现 通过redis     * 使用的是shiro-redis开源插件     */    @Bean    public RedisSessionDAO redisSessionDAO() {        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();        redisSessionDAO.setRedisManager(redisManager;        return redisSessionDAO;    }    /**     * shiro session的管理     */    @Bean    public DefaultWebSessionManager sessionManager() {        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();        sessionManager.setSessionDAO(redisSessionDAO;        return sessionManager;    }}
package com.lyz.user.bean; 

import java.io.Serializable; 

import org.codehaus.jackson.map.annotate.JsonSerialize; 

import com.fasterxml.jackson.databind.PropertyNamingStrategy; 
import com.fasterxml.jackson.databind.annotation.JsonNaming; 

/** 
 * 用户实体类 
 * @author liuyazhuang 
 * 
 */ 
@JsonSerialize 
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)  
public class User implements Serializable{ 
  private static final long serialVersionUID = 1332643889208978231L; 

  /** 
   * id 
   */ 
  private Integer id; 

  /** 
   * 姓名 
   */ 
  private String name; 

  /** 
   * 性别 
   */ 
  private String sex; 

  /** 
   * 年龄 
   */ 
  private Integer age; 

  public User() { 
    super(); 
  } 

  public User(Integer id, String name) { 
    super(); 
    this.id = id; 
    this.name = name; 
  } 

  public User(String name, String sex, Integer age) { 
    super(); 
    this.name = name; 
    this.sex = sex; 
    this.age = age; 
  } 

  public Integer getId() { 
    return id; 
  } 

  public void setId(Integer id) { 
    this.id = id; 
  } 

  public String getName() { 
    return name; 
  } 

  public void setName(String name) { 
    this.name = name; 
  } 

  public String getSex() { 
    return sex; 
  } 

  public void setSex(String sex) { 
    this.sex = sex; 
  } 

  public Integer getAge() { 
    return age; 
  } 

  public void setAge(Integer age) { 
    this.age = age; 
  } 

  @Override 
  public String toString() { 
    return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]"; 
  } 

} 

以上所述是小编给大家介绍的在ssm项目中使用redis缓存查询数据,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

配置自定义Realm
package com.study.shiro;
import com.study.model.Resources;
import com.study.model.User;
import com.study.service.ResourcesService;
import com.study.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * Created by yangqj on 2017/4/21.
 */
public class MyShiroRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Resource
    private ResourcesService resourcesService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user=  SecurityUtils.getSubject().getPrincipal();//User{id=1, username='admin', password='3ef7164d1f6167cb9f2658c07d3c2f0a', enable=1}
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("userid",user.getId;
        List<Resources> resourcesList = resourcesService.loadUserResources;
        // 权限信息对象info,用来存放查出的用户的所有的角色及权限(permission)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        for(Resources resources: resourcesList){            info.addStringPermission(resources.getResurl;        }        return info;    }    //认证    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        //获取用户的输入的账号.        String username = token.getPrincipal();        User user = userService.selectByUsername;        if(user==null) throw new UnknownAccountException();        if (0==user.getEnable {            throw new LockedAccountException(); // 帐号锁定        }        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(                user, //用户                user.getPassword(), //密码                ByteSource.Util.bytes,                getName()  //realm name        );        // 当验证都通过后,把用户信息放在session里        Session session = SecurityUtils.getSubject().getSession();        session.setAttribute("userSession", user);        session.setAttribute("userSessionId", user.getId;        return authenticationInfo;    }}

4、创建UserMapper接口

认证:

shiro的主要模块分别就是授权和认证和会话管理。
我们先讲认证。认证就是验证用户。比如用户登录的时候验证账号密码是否正确。
我们可以把对登录的验证交给shiro。我们执行要查询相应的用户信息,并传给shiro。如下代码则为用户登录:

 @RequestMapping(value="/login",method=RequestMethod.POST)
    public String login(HttpServletRequest request, User user, Model model){
        if (StringUtils.isEmpty(user.getUsername || StringUtils.isEmpty(user.getPassword {
            request.setAttribute("msg", "用户名或密码不能为空!");
            return "login";
        }
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),user.getPassword;
        try {
            subject.login;
            return "redirect:usersPage";
        }catch (LockedAccountException lae) {
            token.clear();
            request.setAttribute("msg", "用户已经被锁定不能登录,请与管理员联系!");
            return "login";
        } catch (AuthenticationException e) {
            token.clear();
            request.setAttribute("msg", "用户或密码不正确!");
            return "login";
        }
    }

可见用户登陆的代码主要就是
subject.login;调用后就会进去我们自定义的realm中的doGetAuthenticationInfo()方法。

 //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取用户的输入的账号.
        String username = (String)token.getPrincipal();
        User user = userService.selectByUsername;
        if(user==null) throw new UnknownAccountException();
        if (0==user.getEnable {
            throw new LockedAccountException(); // 帐号锁定
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user, //用户
                user.getPassword(), //密码
                ByteSource.Util.bytes,
                getName()  //realm name
        );
        // 当验证都通过后,把用户信息放在session里
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute("userSession", user);
        session.setAttribute("userSessionId", user.getId;        return authenticationInfo;
    }

而我们在ShiroConfig中配置了凭证匹配器:

@Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher;
        return myShiroRealm;
    }

 @Bean    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5;
        return hashedCredentialsMatcher;
    }

所以在认证时的密码是加过密的,使用md5散发将密码与盐值组合加密两次。则我们在增加用户的时候,对用户的密码则要进过相同规则的加密才行。
添加用户代码如下:

@RequestMapping(value = "/add")
    public String add(User user) {
        User u = userService.selectByUsername(user.getUsername;
        if(u != null)
            return "error";
        try {
            user.setEnable(1);
            PasswordHelper passwordHelper = new PasswordHelper();
            passwordHelper.encryptPassword;
            userService.save;
            return "success";
        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
    }

PasswordHelper:

package com.study.util;
import com.study.model.User;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;import org.apache.shiro.util.ByteSource;
public class PasswordHelper {
    //private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
    private String algorithmName = "md5";
    private int hashIterations = 2;

    public void encryptPassword(User user) {
        //String salt=randomNumberGenerator.nextBytes().toHex();
        String newPassword = new SimpleHash(algorithmName, user.getPassword(),  ByteSource.Util.bytes(user.getUsername, hashIterations).toHex();
        //String newPassword = new SimpleHash(algorithmName, user.getPassword.toHex();
        user.setPassword(newPassword);

    }
    public static void main(String[] args) {
        PasswordHelper passwordHelper = new PasswordHelper();
        User user = new User();
        user.setUsername("admin");            user.setPassword;        passwordHelper.encryptPassword;        System.out.println;    }}

此接口定义了与MyBatis交互的接口协议,通过此接口与MyBatis框架通信,由MyBatis框架实现对数据库的增删改查操作。

授权:

接下来讲下授权。在自定义relalm中的代码为:

 //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user=  SecurityUtils.getSubject().getPrincipal();//User{id=1, username='admin', password='3ef7164d1f6167cb9f2658c07d3c2f0a', enable=1}
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("userid",user.getId;
        List<Resources> resourcesList = resourcesService.loadUserResources(map);        // 权限信息对象info,用来存放查出的用户的所有的角色及权限(permission)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        for(Resources resources: resourcesList){
            info.addStringPermission(resources.getResurl;
        }
        return info;
    }

从以上代码中可以看出来,我根据用户id查询出用户的权限,放入SimpleAuthorizationInfo。关联表user_role,role_resources,resources,三张表,根据用户所拥有的角色,角色所拥有的权限,查询出分配给该用户的所有权限的url。当访问的链接中配置在shiro中时,或者使用shiro标签,shiro权限注解时,则会访问该方法,判断该用户是否拥有相应的权限。

在ShiroConfig中有如下代码:

 @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
        System.out.println("ShiroConfiguration.shirFilter;
        ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/usersPage");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/css/**","anon");
        filterChainDefinitionMap.put("/js/**","anon");
        filterChainDefinitionMap.put("/img/**","anon");
        filterChainDefinitionMap.put("/font-awesome/**","anon");
        //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        //自定义加载权限资源关系
        List<Resources> resourcesList = resourcesService.queryAll();         for(Resources resources:resourcesList){

            if (StringUtil.isNotEmpty(resources.getResurl {                String permission = "perms[" + resources.getResurl()+ "]";                filterChainDefinitionMap.put(resources.getResurl(),permission);            }        }        filterChainDefinitionMap.put("/**", "authc");        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);        return shiroFilterFactoryBean;    }

该代码片段为配置shiro的过滤器。以上代码将静态文件设置为任何权限都可访问,然后

 List<Resources> resourcesList = resourcesService.queryAll();
         for(Resources resources:resourcesList){

            if (StringUtil.isNotEmpty(resources.getResurl {                String permission = "perms[" + resources.getResurl()+ "]";
                filterChainDefinitionMap.put(resources.getResurl(),permission);
            }
        }

在数据中查询所有的资源,将该资源的url当作key,配置拥有该url权限的用户才可访问该url。
最后加入 filterChainDefinitionMap.put(“/\”,
“authc”);表示其他没有配置的链接都需要认证才可访问。注意这个要放最后面,因为shiro的匹配是从上往下,如果匹配到就不继续匹配了,所以把
/*放到最前面,则 后面的链接都无法匹配到了。
而这段代码是在项目启动的时候加载的。加载的数据是放到内存中的。但是当权限增加或者删除时,正常情况下不会重新启动来,重新加载权限。所以需要调用以下代码的updatePermission()方法来重新加载权限。其实下面的代码有些重复了,可以稍微调整下,我就先这么写了。

package com.study.shiro;
import com.github.pagehelper.util.StringUtil;
import com.study.model.Resources;
import com.study.model.User;
import com.study.service.ResourcesService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.subject.SimplePrincipalCollection;import org.apache.shiro.subject.support.DefaultSubjectContext;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.crazycake.shiro.RedisSessionDAO;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.*;/** * Created by yangqj on 2017/4/30. */@Servicepublic class ShiroService {    @Autowired    private ShiroFilterFactoryBean shiroFilterFactoryBean;    @Autowired    private ResourcesService resourcesService;    @Autowired    private RedisSessionDAO redisSessionDAO;    /**     * 初始化权限     */    public Map<String, String> loadFilterChainDefinitions() {        // 权限控制map.从数据库获取        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();        filterChainDefinitionMap.put("/logout", "logout");        filterChainDefinitionMap.put("/css/**","anon");        filterChainDefinitionMap.put("/js/**","anon");        filterChainDefinitionMap.put("/img/**","anon");        filterChainDefinitionMap.put("/font-awesome/**","anon");        List<Resources> resourcesList = resourcesService.queryAll();        for(Resources resources:resourcesList){            if (StringUtil.isNotEmpty(resources.getResurl {                String permission = "perms[" + resources.getResurl()+ "]";                filterChainDefinitionMap.put(resources.getResurl(),permission);            }        }        filterChainDefinitionMap.put("/**", "authc");        return filterChainDefinitionMap;    }    /**     * 重新加载权限     */    public void updatePermission() {        synchronized (shiroFilterFactoryBean) {            AbstractShiroFilter shiroFilter = null;            try {                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean                        .getObject();            } catch (Exception e) {                throw new RuntimeException(                        "get ShiroFilter from shiroFilterFactoryBean error!");            }            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter                    .getFilterChainResolver();            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver                    .getFilterChainManager();            // 清空老的权限控制            manager.getFilterChains;            shiroFilterFactoryBean.getFilterChainDefinitionMap;            shiroFilterFactoryBean                    .setFilterChainDefinitionMap(loadFilterChainDefinitions;            // 重新构建生成            Map<String, String> chains = shiroFilterFactoryBean                    .getFilterChainDefinitionMap();            for (Map.Entry<String, String> entry : chains.entrySet {                String url = entry.getKey();                String chainDefinition = entry.getValue                        .replace;                manager.createChain(url, chainDefinition);            }            System.out.println("更新权限成功!!");        }    }}
package com.lyz.user.mapper; 

import java.util.List; 

import com.lyz.user.bean.User; 

/** 
 * UserMapper接口 
 * @author liuyazhuang 
 * 
 */ 
public interface UserMapper { 

  /** 
   * 保存用户 
   * @param user 
   */ 
  void saveUser(User user); 

  /** 
   * 获取所有用户列表 
   * @return 
   */ 
  List<User> getAllUser(); 

  /** 
   * 根据id查询用户信息 
   * @param id 
   * @return 
   */ 
  User getUserById(Integer id); 

  /** 
   * 更新用户的名称 
   * @param user 
   */ 
  void renameUser(User user); 

  /** 
   * 根据id删除指定的用户 
   * @param id 
   */ 
  void deleteUserById(Integer id); 
} 
会话管理

这个例子使用了redis保存session。这样可以实现集群的session共享。在ShiroConfig中有代码:

 @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //设置realm.
        securityManager.setRealm(myShiroRealm;
        // 自定义缓存实现 使用redis
        //securityManager.setCacheManager(cacheManager;
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager;
        return securityManager;
    }

配置了自定义session,网上已经有大神实现了 使用redis
自定义session管理,直接拿来用,引入包

<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>2.4.2.1-RELEASE</version>
</dependency>  

然后再配置:

 /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost;
        redisManager.setPort;
        redisManager.setExpire(1800);// 配置缓存过期时间
        redisManager.setTimeout;
        // redisManager.setPassword;
        return redisManager;
    }
    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager;
        return redisCacheManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager;
        return redisSessionDAO;
    }
    /**
     * shiro session的管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();        sessionManager.setSessionDAO(redisSessionDAO;        return sessionManager;    }

RedisConfig:

package com.study.config;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
 * Created by yangqj on 2017/4/30. */@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.pool.max-wait}")
    private long maxWaitMillis;

    @Bean
    public JedisPool redisPoolFactory() {
        Logger.getLogger(getClass.info("JedisPool注入成功!!");        Logger.getLogger(getClass.info("redis地址:" + host + ":" + port);        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();        jedisPoolConfig.setMaxIdle;        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);        return jedisPool;    }}

配置文件 application.properties中加入:

#redis
# Redis服务器地址
spring.redis.host= localhost
# Redis服务器连接端口
spring.redis.port= 6379
# 连接池中的最大空闲连接
spring.redis.pool.max-idle= 8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle= 0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active= 8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait= -1
# 连接超时时间spring.redis.timeout= 0

当然运行的时候要先启动redis。将自己的redis配置在以上配置中。这样session就存在redis中了。
上面ShiroConfig中的securityManager()方法中,我把

//securityManager.setCacheManager(cacheManager;

这行代码注了,是这样的,因为每次在需要验证的地方,比如在subject.hasRole
或 subject.isPermitted、@RequiresRoles 、
shiro:hasPermission=”/users/add”的时候都会调用MyShiroRealm中的doGetAuthorizationInfo()。但是以为这些信息不是经常变的,所以有必要进行缓存。把这行代码的注释打开,的时候都会调用MyShiroRealm中的doGetAuthorizationInfo()的返回结果会被redis缓存。但是这里稍微有个小问题,就是在刚修改用户的权限时,无法立即失效。本来我是使用了ShiroService中的clearUserAuthByUserId()想清除当前session存在的用户的权限缓存,但是没有效果。不知道什么原因。希望哪个大神看到后帮忙弄个解决方法。所以我干脆就把doGetAuthorizationInfo()的返回结果通过spring
cache的方式加入缓存。

  @Cacheable(cacheNames="resources",key="#map['userid'].toString()+#map['type']")    public List<Resources> loadUserResources(Map<String, Object> map) {
        return resourcesMapper.loadUserResources(map);
    }

这样也可以实现,然后在修改权限时加上注解

 @CacheEvict(cacheNames="resources", allEntries=true)

这样修改权限后可以立即生效。其实我感觉这样不好,因为清楚了我是清除了所有用户的权限缓存,其实只要修改当前session在线中被修改权限的用户就行了。
先这样吧,以后再研究下,修改得更好一点。

5、创建UserMapper.xml

按钮控制

在前端页面,对按钮进行细粒度权限控制,只需要在按钮上加上shiro:hasPermission

  <button shiro:hasPermission="/users/add" type="button"  onclick="$('#addUser').modal();" class="btn btn-info" >新增</button>

这里的参数就是我们在ShiroConfig-shirFilter()权限加载时的过滤器
中的value,也就是资源的url。

  filterChainDefinitionMap.put(resources.getResurl(),permission);

此文件中主要实现了UserMapper接口定义的方法,即实现对数据库的增删改查操作。

8.效果图

图片 3

图片 4

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 
<mapper namespace="com.lyz.user.mapper.UserMapper" > 

  <!-- 插入一条城市记录 --> 
  <insert id="saveUser" parameterType="com.lyz.user.bean.User"> 
    insert into user (name, sex, age) 
    values ( #{name}, #{sex}, #{age} ) 
  </insert> 

  <!-- 根据省份名称返回该省份下的所有城市列表 --> 
  <select id="getAllUser" resultType="com.lyz.user.bean.User"> 
    select u.id, u.name, u.sex, u.age from user u   
  </select> 

  <!-- 根据 city_code 查找城市信息 --> 
  <select id="getUserById" resultType="com.lyz.user.bean.User" parameterType="java.lang.Integer"> 
    select u.id, u.name, u.sex, u.age from user u where u.id = #{id} 
  </select> 

  <!-- 重命名城市 -->  
  <update id="renameUser" parameterType="com.lyz.user.bean.User"> 
    update user set name = #{name} where id = #{id} 
  </update>  

  <!-- 删除一个城市记录 -->  
  <delete id="deleteUserById" parameterType="java.lang.Integer">  
    delete from user where id = #{id}  
  </delete>  
</mapper> 

9.运行、下载

下载项目后运行resources下的shiro.sql文件。需要运行redis后运行项目。访问
账号密码:admin admin 或user1 user1.新增的用户也可以登录。

github下载地址:

转载请标明出处:

6、创建UserDao接口

package com.lyz.user.dao; 

import java.util.List; 

import com.lyz.user.bean.User; 

/** 
 * 用户dao接口 
 * @author liuyazhuang 
 * 
 */ 
public interface UserDao { 
   /** 
   * 保存用户 
   * @param user 
   */ 
  void saveUser(User user); 

  /** 
   * 获取所有用户列表 
   * @return 
   */ 
  List<User> getAllUser(); 

  /** 
   * 根据id查询用户信息 
   * @param id 
   * @return 
   */ 
  User getById(Integer id); 

  /** 
   * 更新用户的名称 
   * @param user 
   */ 
  void rename(User user); 

  /** 
   * 根据id删除指定的用户 
   * @param id 
   */ 
  void deleteById(Integer id); 
} 

7、创建UserDao的实现类UserDaoImpl

此类主要实现UserDao中定义的接口,主要实现方法是注入UserMapper接口的实例对象,调用UserMapper接口实例的方法来实现相应的操作。

package com.lyz.user.dao.impl; 
import java.util.List;  
import javax.annotation.Resource;  
import org.springframework.stereotype.Repository;  
import com.lyz.user.bean.User; 
import com.lyz.user.dao.UserDao; 
import com.lyz.user.mapper.UserMapper; 
/** 
 * Dao实现类 
 * @author liuyazhuang 
 * 
 */ 
@Repository 
public class UserDaoImpl implements UserDao { 
  @Resource 
  private UserMapper mUserMapper; 

  @Override 
  public void saveUser(User user) { 
    mUserMapper.saveUser(user); 
  } 

  @Override 
  public List<User> getAllUser() { 
    return mUserMapper.getAllUser(); 
  } 

  @Override 
  public User getById(Integer id) { 
    return mUserMapper.getUserById(id); 
  } 

  @Override 
  public void rename(User user) { 
    mUserMapper.renameUser(user); 
  } 

  @Override 
  public void deleteById(Integer id) { 
    mUserMapper.deleteUserById(id); 
  } 


} 

8、创建UserService接口

package com.lyz.user.service;  
import java.util.List;  
import com.lyz.user.bean.User;  
/** 
 * 用户service接口 
 * @author liuyazhuang 
 * 
 */ 
public interface UserService { 
   /** 
   * 保存用户 
   * @param user 
   */ 
  void saveUser(String name, String sex, Integer age); 

  /** 
   * 获取所有用户列表 
   * @return 
   */ 
  List<User> getAllUser(); 

  /** 
   * 根据id查询用户信息 
   * @param id 
   * @return 
   */ 
  User getUserById(Integer id); 

  /** 
   * 更新用户的名称 
   * @param user 
   */ 
  void renameUser(String name, Integer id); 

  /** 
   * 根据id删除指定的用户 
   * @param id 
   */ 
  void deleteUserById(Integer id); 
} 

9、创建UserService接口实现类UserServiceImpl

package com.lyz.user.service.impl; 
import java.util.List;  
import javax.annotation.Resource;  
import org.springframework.cache.annotation.CacheEvict; 
import org.springframework.cache.annotation.Cacheable; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Propagation; 
import org.springframework.transaction.annotation.Transactional; 
import com.lyz.user.bean.User; 
import com.lyz.user.dao.UserDao; 
import com.lyz.user.service.UserService; 
/** 
 * UserService实现类 
 * @author liuyazhuang 
 * 
 */ 
@Service 
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) 
public class UserServiceImpl implements UserService { 
  @Resource 
  private UserDao mUserDao; 

  @Override 
  @CacheEvict(value = { "saveUser"}, allEntries = true) 
  public void saveUser(String name, String sex, Integer age) { 
    User user = new User(name, sex, age); 
    mUserDao.saveUser(user); 
  } 

  @Override 
  @Cacheable("getAllUser") 
  public List<User> getAllUser() { 
    return mUserDao.getAllUser(); 
  } 

  @Override 
  @Cacheable("getUserById") 
  public User getUserById(Integer id) { 
    return mUserDao.getById(id); 
  } 

  @Override 
  @CacheEvict(value = { "getAllUser", "getUserById" }, allEntries = true) 
  public void renameUser(String name, Integer id) { 
    mUserDao.rename(new User(id, name)); 
  } 

  @Override 
  @CacheEvict(value = { "getAllUser", "getUserById" }, allEntries = true) 
  public void deleteUserById(Integer id) { 
    mUserDao.deleteById(id); 
  } 

} 

10、创建UserController

package com.lyz.user.controller;  
import java.util.List;  
import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.ResponseBody; 
import com.lyz.user.bean.User; 
import com.lyz.user.service.UserService;  
/** 
 * UserController类 
 * @author liuyazhuang 
 * 
 */ 
@Controller 
@RequestMapping("/user") 
public class UserController { 
  private final Log logger = LogFactory.getLog(this.getClass()); 
  @Autowired 
  private UserService mUserService; 

  /** 
   * 保存用户 
   * @param name 
   * @param sex 
   * @param age 
   * @return 
   */ 
  @RequestMapping("/save") 
  @ResponseBody 
  public Integer save(@RequestParam(value="name", defaultValue="") String name, 
      @RequestParam(value="sex", defaultValue="") String sex, 
      @RequestParam(value="age", defaultValue="0") String age){ 
    logger.debug(name); 
    mUserService.saveUser(name, sex, Integer.parseInt(age)); 
    return 1; 
  } 

  /** 
   * 获取所有用户列表 
   * @return 
   */ 
  @RequestMapping("/getall") 
  @ResponseBody 
  public Object getAllUser(){ 
    List<User> users = mUserService.getAllUser(); 
    for(User u : users){ 
      logger.debug(u.toString()); 
    } 
    return users; 
  } 
  /** 
   * 根据用户id获取用户信息 
   * @return 
   */ 
  @RequestMapping("/getUserById") 
  @ResponseBody 
  public Object getUserById(@RequestParam(value="id", defaultValue="0") String id){ 
    User user = mUserService.getUserById(Integer.parseInt(id)); 
    logger.debug(user.toString()); 
    return user; 
  } 

  /** 
   * 根据用户id获取用户信息 
   * @return 
   */ 
  @RequestMapping("/renameUser") 
  @ResponseBody 
  public Integer renameUser(@RequestParam(value="id", defaultValue="0") String id,  
               @RequestParam(value="name", defaultValue="") String name){ 
    logger.debug(id + "=========" + name); 
    mUserService.renameUser(name, Integer.parseInt(id)); 
    return 1; 
  } 
  /** 
   * 根据用户id获取用户信息 
   * @return 
   */ 
  @RequestMapping("/delete") 
  @ResponseBody 
  public Integer delete(@RequestParam(value="id", defaultValue="0") String id){ 
    logger.debug(id); 
    mUserService.deleteUserById(Integer.parseInt(id)); 
    return 1; 
  } 
} 

到此,只要再创建相应的配置文件,我们就可以实现Spring+MyBatis框架的整合了,下面我们创建一个整合Redis的关键类RedisCacheConfig,这个类继承CachingConfigurerSupport。

11、创建RedisCacheConfig类

package com.lyz.cache.redis;  
import java.lang.reflect.Method;  
import org.springframework.cache.CacheManager; 
import org.springframework.cache.annotation.CachingConfigurerSupport; 
import org.springframework.cache.annotation.EnableCaching; 
import org.springframework.cache.interceptor.KeyGenerator; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.data.redis.cache.RedisCacheManager; 
import org.springframework.data.redis.connection.RedisConnectionFactory; 
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 
import org.springframework.data.redis.core.RedisTemplate; 

/** 
 * 以Spring与配置文件来管理的redis缓存配置类 
 * @author liuyazhuang 
 * 
 */ 
@Configuration 
@EnableCaching 
public class RedisCacheConfig extends CachingConfigurerSupport { 

  private volatile JedisConnectionFactory mJedisConnectionFactory; 
  private volatile RedisTemplate<String, String> mRedisTemplate; 
  private volatile RedisCacheManager mRedisCacheManager; 

  public RedisCacheConfig() { 
    super(); 
  } 

  public RedisCacheConfig(JedisConnectionFactory mJedisConnectionFactory, RedisTemplate<String,String> mRedisTemplate, 
      RedisCacheManager mRedisCacheManager) { 
    super(); 
    this.mJedisConnectionFactory = mJedisConnectionFactory; 
    this.mRedisTemplate = mRedisTemplate; 
    this.mRedisCacheManager = mRedisCacheManager; 
  } 

  public JedisConnectionFactory redisConnectionFactory() { 
    return mJedisConnectionFactory; 
  } 

  public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) { 
    return mRedisTemplate; 
  } 

  public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) { 
    return mRedisCacheManager; 
  } 

  @Bean 
  public KeyGenerator customKeyGenerator() { 
    return new KeyGenerator() { 
      @Override 
      public Object generate(Object o, Method method, Object... objects) { 
        StringBuilder sb = new StringBuilder(); 
        sb.append(o.getClass().getName()); 
        sb.append(method.getName()); 
        for (Object obj : objects) { 
          sb.append(obj.toString()); 
        } 
        return sb.toString(); 
      } 
    }; 
  } 
}