首先感谢纯洁的微笑大神的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真的很好用