文章目录
一、权限管理
不同身份的用户进入到系统所能完成的操作是不同的,我们对不同用户进行的可执行的操作管理称之为权限管理
1.1 实现方法
①基于主页的实现方法(权限通过主页来限制)
适用于权限管理比较单一,用户少,权限固定
②基于用户权限的实现方法
可以实现权限的动态分配,但是不够灵活
③RBAC 基于角色的访问控制(Role-Based Access Control)
二、Shiro工作原理
2.1Shiro核心功能
Authentication 认证 ,Authorization 授权,Session Management 会话管理功能,Cryptography 密码管理
-
Authentication 认证,验证用户是否有相应的身份—登录认证
-
Authorization 授权,即权限验证,对已经通过认证的用户检查是否具有某个权限或角色,从而控制是否能够进行某种操作
-
Session Management 会话管理功能,用户认证成功之后创建会话,在没有退出之前,当前用户的所有信息都会保存在这个会话中
-
Cryptography 密码管理,对敏感信息进行加密处理,shiro就提供这种密钥。
-
支持特性:
Web Support —shiro提供过滤器。
Caching 缓存支持,shiro可以缓存用户信息以及用户角色权限信息;提高执行效率。
Concurrency shiro支持多线程应用。
Testing 提供测试支持。
2.2 核心组件
三大核心组件: Subject , Security Manager, Realms
- Subject, 表示待认证和授权用户。
- Security Manager,它是shiro框架的核心,shiro就是通过Security Manager来进行内部实例的管理,并通过它提供安全管理的各种服务。
- Security ,相当于shiro进行认证和授权的数据源,充当了shiro与安全数据的“桥梁”。
shiro的工作示意图 :
三、springboot整合shiro
3.1创建项目
包含:mysql, springweb, thymeleaf, druid和mybatis
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- druid starter-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
3.2 yml配置:
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/shirotest?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: chen123456
initial-size: 1
min-idle: 1
max-active: 20
server:
port: 8081
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.vector.beens
整合shiro:
<!-- shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.0</version>
</dependency>
3.3 配置shiro的ini拦截器
package com.vector.config;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public IniRealm getIniRealm(){
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
return iniRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(IniRealm iniRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager要完成需要realm
securityManager.setRealm(iniRealm);
return securityManager;
}
// 过滤器
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//shiro认证和授权是需要securityManager的
filter.setSecurityManager(securityManager);
//拦截规则
/**
* anon 匿名用户可访问
* authc 认证用户可访问
* user 使用RemeberMe的用户可访问
* perms 对应权限可访问
* role 对应角色可访问
*/
Map<String,String> filterMap = new HashMap<>();
//根目录不拦截
filterMap.put("/","anon");
//。。。
filterMap.put("/login.html","anon");
filterMap.put("/regist.html","anon");
filterMap.put("/user/login","anon");
filterMap.put("/user/regist","anon");
filterMap.put("/static/**","anon");
//认证用户可访问
filterMap.put("/**","authc");
filter.setFilterChainDefinitionMap(filterMap);
// 设置默认登陆页面
filter.setLoginUrl("/login.html");
// 设置未授权访问页面跳转路径
filter.setUnauthorizedUrl("/login.html");
return filter;
}
}
3.4 基于数据库jdbc的shiro配置
package com.vector.config;
@Configuration
public class ShiroConfig {
@Bean
public JdbcRealm getJdbcRealm(DataSource dataSource){
JdbcRealm jdbcRealm = new JdbcRealm();
//会自动从数据库中查询用户及权限数据(数据库表结构要符合JdbcRealm的规范)
jdbcRealm.setDataSource(dataSource);
//默认开启认证功能,需要手动开启授权功能
jdbcRealm.setPermissionsLookupEnabled(true);
return jdbcRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(JdbcRealm jdbcRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager要完成需要realm
securityManager.setRealm(jdbcRealm);
return securityManager;
}
// 过滤器
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//shiro认证和授权是需要securityManager的
filter.setSecurityManager(securityManager);
//拦截规则
Map<String,String> filterMap = new HashMap<>();
//根目录不拦截
filterMap.put("/","anon");
filterMap.put("/login.html","anon");
filterMap.put("/regist.html","anon");
filterMap.put("/user/login","anon");
filterMap.put("/user/regist","anon");
filterMap.put("/static/**","anon");
//认证用户可访问
filterMap.put("/**","authc");
filter.setFilterChainDefinitionMap(filterMap);
// 设置默认登陆页面
filter.setLoginUrl("/login.html");
// 设置未授权访问页面跳转路径
filter.setUnauthorizedUrl("/login.html");
return filter;
}
}
3.4 serviceiml中代码
// 登录认证代码
@Service
public class UserServiceImpl implements UserService {
@Override
public void chickLogin(String username, String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
}
}
四、shiro标签的使用
-
jsp中的引用
<%@ taglib prefix="shiro" url="http://shiro.apache.org/tags" %>
-
Thymeleaf模板中引用
-
导入对shiro的依赖
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
在ShiroConfig中配置shiro的
@Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } // ...
引入shiro命名空间
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> ... </html>
-
常用标签的使用
<shiro:guest>该标签中的内容是在未登录状态下显示</shiro:guest>
<shiro:user>已登陆用户可访问</shiro:user>
用户[<shiro:principal/>]欢迎登录 //获取用户
// 判断角色
当前用户是<shiro:hasRole name="admin">超级管理员</shiro:hasRole>
<shiro:hasRole name="cmanager">仓库管理员</shiro:hasRole>
<shiro:hasRole name="xmanage">销售管理员</shiro:hasRole>
// 判断用到的权限
仓管
<ul>
<shiro:hasPermission name="sys:c:save">
<li><a href="#">保存</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="sys:c:delete">
<li><a href="#">删除</a></li>
</shiro:hasPermission>
</ul>
五、Dao层实现
1.根据用户名查询用户信息
2.shiro进行授权管理需要当前用户的角色和权限
。根据用户名查询当前用户的角色列表(联合查询)
。根据用户查询当前用户的权限列表(联合查询)
六、自定义realm的配置
6.1 MyRealm类
public class MyRealm extends AuthorizingRealm {
@Resource
private UserDao userDao;
@Resource
private RoleDao roleDao;
@Resource
private PermissionDao permissionDao;
public String getName(){
return "myRealm";
}
/**
* 获取授权数据
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取用户的用户名
String username = (String) principalCollection.iterator().next();
//查询角色列表
Set<String> roleNames = roleDao.queryRoleNameByUserName(username);
//查询当前用户的权限列表
Set<String> ps = permissionDao.queryPermissionByUserName(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roleNames);
info.setStringPermissions(ps);
return info;
}
/**
* 获取认证数据
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 从token中获取用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
// 根据用户名从数据库中查询安全数据
User user = userDao.queryUserByUserName(username);
if (user==null){
return null;
}
AuthenticationInfo info = new SimpleAuthenticationInfo(
username, //当前用户名
user.getUserPwd(), // 从数据库中查出的密码
getName()
);
return info;
}
}
6.2 config中
@Configuration
public class ShiroConfig {
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
@Bean
public MyRealm getMyRealm(){
MyRealm myRealm = new MyRealm();
return myRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager要完成需要realm
securityManager.setRealm(myRealm);
return securityManager;
}
// 过滤器
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//shiro认证和授权是需要securityManager的
filter.setSecurityManager(securityManager);
//拦截规则
Map<String,String> filterMap = new HashMap<>();
//根目录不拦截
filterMap.put("/","anon");
filterMap.put("/login.html","anon");
filterMap.put("/regist.html","anon");
filterMap.put("/user/login","anon");
filterMap.put("/user/regist","anon");
filterMap.put("/static/**","anon");
//认证用户可访问
filterMap.put("/**","authc");
filter.setFilterChainDefinitionMap(filterMap);
// 设置默认登陆页面
filter.setLoginUrl("/login.html");
// 设置未授权访问页面跳转路径
filter.setUnauthorizedUrl("/login.html");
return filter;
}
}
七、shiro的mad5加密
// shiro.config中添加内容
@Bean
public HashedCredentialsMatcher getHashedCredentialsMatcher(){
//指定加密规则
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//加密方式
matcher.setHashAlgorithmName("md5");
//hash加密次数
matcher.setHashIterations(1);
return matcher;
}
@Bean
public MyRealm getMyRealm(HashedCredentialsMatcher matcher){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(matcher);
return myRealm;
}
...
// 如果数据库中的密码加了盐则需要 (MyRealm.java中)
/**
* 获取认证数据
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 从token中获取用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
// 根据用户名从数据库中查询安全数据
User user = userDao.queryUserByUserName(username);
if (user==null){
return null;
}
AuthenticationInfo info = new SimpleAuthenticationInfo(
username, //当前用户名
user.getUserPwd(), // 从数据库中查出的密码
ByteSource.Util.bytes(user.getPwdSalt()),
getName()
);
return info;
}
八、授权处理
8.1 配置注解支持
在config中配置
// 注解拦截
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
//注解权限
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
//调用注解,例如:
@RequiresPermissions("sys:c:save")
8.2 全局异常处理
@ControllerAdvice
public class GlobalExceptionHander {
@ExceptionHandler
public String doException(Exception e){
if (e instanceof AuthorizationException){
return "";
}
return "";
}
}
九、缓存的使用
减少数据库的访问压力
- 导入依赖
<!-- boot缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 第三方缓存-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- shiro支持缓存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.7.0</version>
</dependency>
- ehcache.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="D:\TEMP" />
<cache name="users" timeToliveSecond="300" maxEntriesLocalHeap="1000"/>
<defaultCache name="defaultCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
<!-- 是否存入磁盘-->
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
<!-- 缓存淘汰策略LRU(最近最久未使用) LFU(最少使用)-->
memoryStoreEvictionPolicy="LRU" />
</ehcache>
- ShiroConfig.java配置缓存
//缓存配置
@Bean
public EhCacheManager getEhCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm,EhCacheManager ehCacheManager){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager要完成需要realm
securityManager.setRealm(myRealm);
securityManager.setCacheManager(ehCacheManager);
return securityManager;
}