spring boot2.0+shiro+mybatis多数据源+druid连接池项目整合

首先感谢纯洁的微笑大神的springboot教程,本项目很多地方都是参考写出,贴出大神自己建立的学习网站http://www.ityouknow.com/spring-boot.html

 

关于整合

   网上关于springboot2.0和shiro+myabtis整合的案例很少,大神的教程也是用jpa编写,jpa很方便,但是还有很多人用mybatis,加之刚学习完mybatis多数据源整合和druid连接池监控配置,所以算是阶段性记录。

项目目录

POM文件

<?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.example</groupId>
	<artifactId>mybatisdemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>mybatisdemo</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.4.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-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
		<dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        </dependency>
		<dependency>
			<groupId> org.springframework.boot </groupId>
			<artifactId> spring-boot-configuration-processor </artifactId>
			<optional> true </optional>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.0</version>
		</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

application.yml文件

server:
  port: 8080

spring:
  datasource:
    druid:
      test1:
        #配置监控统计拦截的filters,去掉后监控界面SQL无法进行统计,'wall'用于防火墙
        filters: stat,wall
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
        username: root
        password: root
        #初始化大小
        initial-size: 1
        #最小连接数
        min-idle: 1
        #最大连接数
        max-active: 20
        #获取连接等待超时时间
        max-wait: 60000
        #间隔多久才进行一次检测,检测需要关闭的空闲连接,单位毫秒
        time-between-eviction-runs-millis: 60000
        #一个连接在池中最小生存的时间,单位是毫秒
        min-evictable-idle-time-millis: 30000
        #测试语句是否执行正确
        validation-query: SELECT 'x'
        #指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
        test-while-idle: true
        #借出连接时不要测试,否则很影响性能
        test-on-borrow: false
        test-on-return: false
        #打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
        pool-prepared-statements: false
        #与Oracle数据库PSCache有关,再druid下可以设置的比较高
        max-pool-prepared-statement-per-connection-size: 20
      #数据源2
      test2:
        filters: stat,wall
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/mytest2?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
        username: root
        password: root
        initial-size: 1
        min-idle: 1
        max-active: 20
        max-wait: 60000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 30000
        validation-query: SELECT 'x'
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
        pool-prepared-statements: false
        max-pool-prepared-statement-per-connection-size: 20

  thymeleaf:
    cache: false
    mode: LEGACYHTML5

数据源1配置

package com.example.mybatisdemo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

@Configuration
@MapperScan(basePackages = "com.example.mybatisdemo.dao1",sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class DataSource1Config {

    @Bean(name = "test1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.druid.test1")
    @Primary
    public DruidDataSource test1DataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "test1SqlSessionFactory")
    @Primary
    public SqlSessionFactory test1sqlSessionFactory(@Qualifier("test1DataSource") DruidDataSource druidDataSource) throws Exception{
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(druidDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return bean.getObject();
    }

    @Bean(name = "test1TransactionManager")
    @Primary
    public DataSourceTransactionManager test1TransactionManager(@Qualifier("test1DataSource")DruidDataSource druidDataSource){
        return new DataSourceTransactionManager(druidDataSource);
    }

    @Bean(name = "test1SqlSessionTemplate")
    @Primary
    public SqlSessionTemplate test1SqlSessionTemplate(@Qualifier("test1SqlSessionFactory")SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

数据源2配置

package com.example.mybatisdemo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

@Configuration
@MapperScan(basePackages = "com.example.mybatisdemo.dao2",sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class DataSource2Config {

    @Bean(name = "test2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.druid.test2")
//    @Primary
    public DruidDataSource test2DataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "test2SqlSessionFactory")
//    @Primary
    public SqlSessionFactory test2sqlSessionFactory(@Qualifier("test2DataSource") DruidDataSource druidDataSource) throws Exception{
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(druidDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return bean.getObject();
    }

    @Bean(name = "test2TransactionManager")
//    @Primary
    public DataSourceTransactionManager test2TransactionManager(@Qualifier("test2DataSource")DruidDataSource druidDataSource){
        return new DataSourceTransactionManager(druidDataSource);
    }

    @Bean(name = "test2SqlSessionTemplate")
//    @Primary
    public SqlSessionTemplate test2SqlSessionTemplate(@Qualifier("test2SqlSessionFactory")SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

Druid连接池监控配置

package com.example.mybatisdemo.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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;

@Configuration
public class DruidConfig {

    /**
     * 注册servlet信息,配置监控图
     *
     */

    @Bean
    @ConditionalOnMissingBean
    public ServletRegistrationBean druidServlet(){
        ServletRegistrationBean servletRegistrationBean =
            new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
        //白名单
        servletRegistrationBean.addInitParameter("allow","192.168.6.195");
        //IP黑名单(存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
        servletRegistrationBean.addInitParameter("deny","192.168.6.73");
        //用于登陆的账号密码
        servletRegistrationBean.addInitParameter("loginUsername","admin");
        servletRegistrationBean.addInitParameter("loginPassword","admin");
        //是否能重置数据
        servletRegistrationBean.addInitParameter("resetEnable","true");
        return servletRegistrationBean;
    }
    /**
     *
     * 注册filter信息,用于拦截
     */

    @Bean
    @ConditionalOnMissingBean
    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;
    }
}

 

关于RABC角色访问控制

RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

建表过程

CREATE TABLE `sys_permission` (
  `id` int(11) NOT NULL,
  `available` bit(1) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `parent_id` bigint(20) DEFAULT NULL,
  `parent_ids` varchar(255) DEFAULT NULL,
  `permission` varchar(255) DEFAULT NULL,
  `resource_type` enum('menu','button') DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL,
  `available` bit(1) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `role` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
CREATE TABLE `user_info` (
  `uid` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  `state` tinyint(4) NOT NULL,
  `username` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
CREATE TABLE `sys_role_permission` (
  `permission_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  KEY `FK9q28ewrhntqeipl1t04kh1be7` (`role_id`),
  KEY `FKomxrs8a388bknvhjokh440waq` (`permission_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
CREATE TABLE `sys_user_role` (
  `role_id` int(11) NOT NULL,
  `uid` int(11) NOT NULL,
  KEY `FKgkmyslkrfeyn9ukmolvek8b8f` (`uid`),
  KEY `FKhh52n8vd4ny9ff4x9fb8v65qx` (`role_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4

插入测试数据 

INSERT INTO `user_info` (`uid`,`username`,`name`,`password`,`salt`,`state`) VALUES ('1', 'admin', '管理员', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0);
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (1,0,'用户管理',0,'0/','userInfo:view','menu','userInfo/userList');
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (2,0,'用户添加',1,'0/1','userInfo:add','button','userInfo/userAdd');
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (3,0,'用户删除',1,'0/1','userInfo:del','button','userInfo/userDel');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (1,0,'管理员','admin');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (2,0,'VIP会员','vip');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (3,1,'test','test');
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (1,1);
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (2,1);
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (3,2);
INSERT INTO `sys_user_role` (`role_id`,`uid`) VALUES (1,1);

实体类(getter setter方法自行实现)

package com.example.mybatisdemo.domain;

public class Permission {

    private Integer id;
    private Boolean available;
    private String name;
    private Long parent_id;
    private String parent_ids;
    private String permission;
    private String resource_type;
    private String url;}
package com.example.mybatisdemo.domain;

public class Role {

    private Integer id;
    private Boolean available;
    private String description;
    private String role;
}
package com.example.mybatisdemo.domain;

public class UserInfo {

    private Integer uid;

    private String name;

    private String username;

    private String password;

    private String salt;

    private byte state;

    //密码加盐
    public String getCredentialsSalt(){
        return this.username+this.salt;
    }
}

盐值由数据库中的salt和账号组合而成

DAO层

因为多数据源配置,dao层有两个并分开使用,可以一个设计数据一个设计用户验证,这里只实现用户验证放dao1中

package com.example.mybatisdemo.dao1;

import com.example.mybatisdemo.domain.Permission;
import com.example.mybatisdemo.domain.Role;
import com.example.mybatisdemo.domain.UserInfo;

import java.util.List;

public interface UserInfoDao {

    UserInfo selectByUsername(String username);

    List<Integer> selectRoleidByUid(Integer uid);

    Role selectRoleById(Integer id);

    List<Integer> selectPermissionidByRoleid(Integer roleid);

    Permission selectPermissionById(Integer permissionid);
}

Mapper实现

<?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.example.mybatisdemo.dao1.UserInfoDao">

    <sql id="BASE_COLUMN">
        uid,name,password,salt,state,username
    </sql>
    <sql id="BASE_TABLE">
        user_info
    </sql>
    <sql id="USERNAME">
        username
    </sql>
    <sql id="ROLEID">
        role_id
    </sql>
    <sql id="USERROLE">
        sys_user_role
    </sql>
    <sql id="ROLE_COLUMN">
        id,available,description,role
    </sql>
    <sql id="ROLETABLE">
        sys_role
    </sql>
    <sql id="PERMISSIONID">
      permission_id
    </sql>
    <sql id="ROLEPERMISSION">
      sys_role_permission
    </sql>
    <sql id="PERMISSIONCOLUNMN">
        id,available,name,parent_id,parent_ids,permission,resource_type,url
    </sql>
    <sql id="PERMISSIONTABLE">
        sys_permission
    </sql>
    <select id="selectByUsername" resultType="com.example.mybatisdemo.domain.UserInfo" parameterType="java.lang.String">
        select
        <include refid="BASE_COLUMN"/>
        FROM
        <include refid="BASE_TABLE"/>
        WHERE
        <include refid="USERNAME"/>
        <trim prefix="=">
          #{username,jdbcType=VARCHAR}
        </trim>
    </select>
    <select id="selectRoleidByUid" resultType="java.lang.Integer" parameterType="java.lang.Integer">
        SELECT
        <include refid="ROLEID"/>
        FROM
        <include refid="USERROLE"/>
        WHERE uid
        <trim prefix="=">
            #{uid,jdbcType=INTEGER}
        </trim>
    </select>
    <select id="selectRoleById" resultType="com.example.mybatisdemo.domain.Role" >
        SELECT
        <include refid="ROLE_COLUMN"/>
        FROM
        <include refid="ROLETABLE"/>
        WHERE id
        <trim prefix="=">
            #{id, jdbcType=INTEGER}
        </trim>
    </select>
    <select id="selectPermissionidByRoleid" resultType="java.lang.Integer" parameterType="java.lang.Integer">
        SELECT
        <include refid="PERMISSIONID"/>
        FROM
        <include refid="ROLEPERMISSION"/>
        where
        <include refid="ROLEID"/>
        <trim prefix="=">
            #{roleid, jdbcType=INTEGER}
        </trim>
    </select>
    <select id="selectPermissionById" resultType="com.example.mybatisdemo.domain.Permission">
        SELECT 
        <include refid="PERMISSIONCOLUNMN"/>
        FROM
        <include refid="PERMISSIONTABLE"/>
        WHERE id
        <trim prefix="=">
          #{id, jdbcType=INTEGER}
        </trim>
    </select>
</mapper>

Service层

package com.example.mybatisdemo.service;

import com.example.mybatisdemo.domain.Permission;
import com.example.mybatisdemo.domain.Role;
import com.example.mybatisdemo.domain.UserInfo;

import java.util.List;

public interface UserInfoService {

    UserInfo findByUsername(String username);

    Role findRoleById(Integer id);

    List<Integer> findRoleidByUid(Integer uid);

    List<Integer> findPermissionidByRoleid(Integer roleid);

    Permission findPermissionById(Integer permissionid);
}

Service层实现

package com.example.mybatisdemo.service.impl;

import com.example.mybatisdemo.dao1.UserInfoDao;
import com.example.mybatisdemo.domain.Permission;
import com.example.mybatisdemo.domain.Role;
import com.example.mybatisdemo.domain.UserInfo;
import com.example.mybatisdemo.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service(value = "userInfoService")
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoDao userInfoDao;

    @Override
    public UserInfo findByUsername(String username) {
        return userInfoDao.selectByUsername(username);
    }

    @Override
    public Role findRoleById(Integer id) {
        return userInfoDao.selectRoleById(id);
    }

    @Override
    public List<Integer> findRoleidByUid(Integer uid) {
        return userInfoDao.selectRoleidByUid(uid);
    }

    @Override
    public List<Integer> findPermissionidByRoleid(Integer roleid) {
        return userInfoDao.selectPermissionidByRoleid(roleid);
    }

    @Override
    public Permission findPermissionById(Integer permissionid) {
        return userInfoDao.selectPermissionById(permissionid);
    }
}

 Shiro配置

package com.example.mybatisdemo.config;

import com.example.mybatisdemo.domain.UserInfo;
import com.example.mybatisdemo.service.UserInfoService;
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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import javax.annotation.Resource;

public class MyshiroRealm extends AuthorizingRealm {

    @Resource
    private UserInfoService userInfoService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserInfo userInfo = (UserInfo) principalCollection.getPrimaryPrincipal();
        for (Integer integer : userInfoService.findRoleidByUid(userInfo.getUid())){
            authorizationInfo.addRole(userInfoService.findRoleById(integer).getRole());
            for (Integer id : userInfoService.findPermissionidByRoleid(integer)){
                authorizationInfo.addStringPermission(userInfoService.findPermissionById(id).getPermission());
            }
        }
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("我在执行登陆验证-----");
        String username = (String)authenticationToken.getPrincipal();
        System.out.println("token.getCredentials()-----"+authenticationToken.getCredentials());
        UserInfo userInfo = userInfoService.findByUsername(username);
        System.out.println("userInfo-----"+userInfo);
        if (userInfo == null){
            return null;
        }
        System.out.println(userInfo.getCredentialsSalt());
        System.out.println(userInfo.getPassword());
        SimpleAuthenticationInfo authorizationInfo = new SimpleAuthenticationInfo(
                userInfo,
                userInfo.getPassword(),
                ByteSource.Util.bytes(userInfo.getCredentialsSalt()),
                getName()
        );
        return authorizationInfo;
    }
}
package com.example.mybatisdemo.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shriofilter(SecurityManager securityManager){

        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        //配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/registered","anon");
        filterChainDefinitionMap.put("/nofilter/**","anon");
        //配置退出过滤器 其中具体代码已经被Shiro帮忙实现了
        filterChainDefinitionMap.put("/logout","logout");
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**","authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        //登陆成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * )
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//md5散列算法
        hashedCredentialsMatcher.setHashIterations(2);//散列次数
        return hashedCredentialsMatcher;
    }

    @Bean
    public MyshiroRealm myshiroRealm(){
        MyshiroRealm myshiroRealm = new MyshiroRealm();
        myshiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myshiroRealm;
    }

    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myshiroRealm());
        return securityManager;
    }

    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     * @return
     */

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
        mappings.setProperty("UnauthorizedException","403");
        r.setExceptionMappings(mappings);  // None by default
        r.setDefaultErrorView("error");    // No default
        r.setExceptionAttribute("ex");     // Default is "exception"
        //r.setWarnLogCategory("example.MvcLogger");     // No default
        return r;
    }
}

 

Web层

package com.example.mybatisdemo.web;

import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Controller
public class HomeController {

    @RequestMapping({"/","/index"})
    public String index(){
        return "/index";
    }

    @RequestMapping("/login")
    public String login(HttpServletRequest request, Map<String,Object> map) throws Exception{
        System.out.println("登录页面");
        // 登录失败从request中获取shiro处理的异常信息。
        // shiroLoginFailure:就是shiro异常类的全类名.
        String exception = (String) request.getAttribute("shiroLoginFailure");
        System.out.println("exception="+exception);
        String msg = "";
        if (exception!=null){
            if (UnknownAccountException.class.getName().equals(exception)){
                System.out.println("账号不存在");
                msg = "账号不存在";
            } else if (IncorrectCredentialsException.class.getName().equals(exception)){
                System.out.println("密码不正确");
            } else if ("kaptchaValidateFailed".equals(exception)){
                System.out.println("验证码不正确");
            } else {
                msg ="else  "+exception;
                System.out.println("else  "+exception);
            }
        }
        map.put("msg",msg);
        return "login";
    }

    @RequestMapping("/403")
    public String unauthorizaRole(){
        System.out.println("没有权限");
        return "/403";
    }

    @RequestMapping("/registered")
    public String registered(){
        return "/registered";
    }
}

 

package com.example.mybatisdemo.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/nofilter")
public class NoFiltercController {

    @RequestMapping("/nofilter")
    public String nofilter(){
        return "static/nofilter";
    }
}

在/nofilter下的所有链接都可以匿名访问

package com.example.mybatisdemo.web;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/userInfo")
public class UserInfoController {

    @RequestMapping("/userList")
    @RequiresPermissions("userInfo:view")
    public String userInfo(){
        return "userInfo";
    }

    @RequestMapping("/userAdd")
    @RequiresPermissions("userInfo:add")
    public String userInfoAdd(){
        return "userInfoAdd";
    }

    @RequestMapping("/userDel")
    @RequiresPermissions("UserInfo:del")
    public String userInfoDel(){
        return "userInfoDel";
    }
}

/userinfo下的所有连接都需要验证登陆后实现

关于页面

除了login.html之外都很简单,贴出login.html和index.html,其他的网页都可以仿照index.html实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
错误信息:<h4 th:text="${msg}"></h4>
<form action="" method="post">
    <p>账号:<input type="text" name="username" value="admin"/></p>
    <p>密码:<input type="text" name="password" value="123456"/></p>
    <p><input type="submit" value="登录"/>
        <button onclick="window.location.href='/registered'" type="button">注册
        </button>
    </p>

</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>index</h1>
</body>
</html>

测试

1、编写好后就可以启动程序,访问http://localhost:8080/userInfo/userList页面,由于没有登录就会跳转到http://localhost:8080/login页面,登陆之后就会跳转到index页面,登录后,直接在浏览器中输入http://localhost:8080/userInfo/userList访问就会看到用户信息。上面这些操作时候触发MyShiroRealm.doGetAuthenticationInfo()这个方法,也就是登录认证的方法。

2、登录admin账户,访问:http://127.0.0.1:8080/userInfo/userAdd显示用户添加界面,访问http://127.0.0.1:8080/userInfo/userDel显示403没有权限。上面这些操作时候触发MyShiroRealm.doGetAuthorizationInfo()这个方面,也就是权限校验的方法。

3、修改admin不同的权限进行测试

 

4、登陆http://localhost:8080/druid 可以看到sql语句监控,账号admin密码admin

阿里云druid真的很好用

 

至此项目结束,网上基本搜不到这些整合,我也是初学者 靠自己摸索出来的经验,希望能帮助大家,做个参考,有些地方写的很麻烦。注册没有写完,加密可以用new Md5Hash("source","salt",2);来获取,shiro没有关于注册的封装,所以注册须自己实现。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值