1.什么是shiro(以下内容来源于百度,红色字体为重点内容,主要使用的功能)
Apache Shiro是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。
Apache Shiro的首要目标是易于使用和理解。安全通常很复杂,甚至让人感到很痛苦,但是Shiro却不是这样子的。一个好的安全框架应该屏蔽复杂性,向外暴露简单、直观的API,来简化开发人员实现应用程序安全所花费的时间和精力。
Shiro能做什么呢?
- 验证用户身份
- 用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限
- 在非 web 或 EJB 容器的环境下可以任意使用Session API
- 可以响应认证、访问控制,或者 Session 生命周期中发生的事件
- 可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)
- 支持单点登录(SSO)功能
- 支持提供“Remember Me”服务,获取用户关联信息而无需登录
…
等等——都集成到一个有凝聚力的易于使用的API。
Shiro 致力在所有应用环境下实现上述功能,小到命令行应用程序,大到企业应用中,而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去,但也可以在它们之外的任何环境下开箱即用。
Apache Shiro Features 特性
Apache Shiro是一个全面的、蕴含丰富功能的安全框架。下图为描述Shiro功能的框架图:
Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石。那么就让我们来看看它们吧:
- Authentication(认证):用户身份识别,通常被称为用户“登录”
- Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
- Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
- Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持:
- Web支持:Shiro 提供的 web 支持 api ,可以很轻松的保护 web 应用程序的安全。
- 缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。
- 并发:Apache Shiro 支持多线程应用程序的并发特性。
- 测试:支持单元测试和集成测试,确保代码和预想的一样安全。
- “Run As”:这个功能允许用户假设另一个用户的身份(在许可的前提下)。
- “Remember Me”:跨 session 记录用户的身份,只有在强制需要时才需要登录。
注意: Shiro不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro
High-Level Overview 高级概述
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
2.引入依赖
3.添加shiroConfig配置类,相当于配置一个SSM项目中配置了一个XML文件
package com.example.hz.config.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.web.servlet.HandlerExceptionResolver;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 整合shiro配置文件 @Configuration相当于配置一个xml文件,可以理解为一个beans
* 1.设置 ShiroFilterFactoryBean
* 2.设置 SecurityManager
* 3.设置 Realm
* huazhou.add 20190623
*/
//该注解将当前类定义为配置类,相当于xml中的beans
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean
* 是个拦截器器,请求在进入controller之前进行过滤
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Autowired SecurityManager manager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
System.out.println("【Shiro拦截器】工厂类注入开始");
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(manager);
//设置shiro内置请求过滤器
/**
* 过滤器规则:
* anon:无需登录
* authc:需要登陆认证才可访问
* user:如果使用rememberMe功能可以直接访问
* perms:得到资源权限才可以访问
* role:必须得到角色权限才可以访问
*/
Map<String, String> filterMap = new LinkedHashMap<String, String>();
filterMap.put("/index","anon");
filterMap.put("/logout","logout");
filterMap.put("/common/**","anon");
filterMap.put("/css/**","anon");
filterMap.put("/img/**","anon");
filterMap.put("/js/**","anon");
filterMap.put("/login","anon");
//其余接口统一需要拦截,放在最后保证之前设置的不被影响
filterMap.put("/**","authc");
//未登录用户跳转页面(登陆页面)
shiroFilterFactoryBean.setLoginUrl("/login.html");
//登陆成功后跳转的页面
shiroFilterFactoryBean.setSuccessUrl("/index.html");
//退出页面
//设置无权限跳转页面
//shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
System.out.println("【Shiro拦截器】工厂类注入结束");
return shiroFilterFactoryBean;
}
/**
* SecurityManager负责管理Realm,可以有多个规则
*/
@Bean
public SecurityManager getSecurityManager(@Autowired MyRealm myRealm,
@Autowired RememberMeManager rememberMeManager,
@Autowired CacheManager cacheManager){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm); //权限验证
securityManager.setCacheManager(cacheManager); //缓存
securityManager.setRememberMeManager(rememberMeManager); //记住我
//SecurityManager(org.apache.shiro.web.mgt.DefaultWebSecurityMana),注意引包问题
return securityManager;
}
/**
* Realm(放入spring容器管理)
* 认证授权规则,自己实现规则
*/
@Bean
public MyRealm getRealm(@Autowired HashedCredentialsMatcher hashedCredentialsMatcher){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return myRealm;
}
/**
* 密码校验规则HashedCredentialsMatcher
* 这个类是为了对密码进行编码的 ,
* 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
* 这个类也负责对form里输入的密码进行编码
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//加密方式
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
/**
* 记住我
* @return
*/
@Bean
public RememberMeManager rememberMeManager() {
Cookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);//通过js脚本将无法读取到cookie信息
cookie.setMaxAge(60 * 60 * 24);//cookie保存一天
CookieRememberMeManager manager=new CookieRememberMeManager();
manager.setCookie(cookie);
return manager;
}
/**
* 缓存
*/
@Bean
public CacheManager cacheManager() {
MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
return cacheManager;
}
/**
* 注册全局异常处理
* @return
*/
@Bean(name = "exceptionHandler")
public HandlerExceptionResolver handlerExceptionResolver(){
return new GlobalExceptionResolver();
}
}
4.实现自己的域MyRealm
package com.example.hz.config.shiro;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.hz.entity.HzUser;
import com.example.hz.service.IHzUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 创建自己的realm,可以是多个,必须继承 AuthorizingRealm
* 自定义用户登陆认证及授权规则
* * 1.登陆认证 doGetAuthenticationInfo
* * 2.授权 doGetAuthorizationInfo
* huazhou.add 20180623
*
*/
@Slf4j
public class MyRealm extends AuthorizingRealm {
@Autowired
private IHzUserService iHzUserService;
/**
* 执行授权逻辑
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("【MyRealm授权】:执行授权逻辑开始");
//1.获取登陆用户信息
HzUser user = (HzUser) principalCollection.getPrimaryPrincipal();
//2.根据用户信息查询用户权限
//3.添加用户角色所应有的权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
System.out.println("【MyRealm授权】:执行授权逻辑结束");
return simpleAuthorizationInfo;
}
/**
* 1.
* 执行认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("【MyRealm登陆认证】:执行认证逻辑开始");
//1.获取登陆用户信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//2.查询信息是否存在
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("login_name",token.getUsername());
HzUser user = iHzUserService.getOne(wrapper);
if(null == user ){
throw new UnknownError("用户不存在");
}
if(0 == user.getStatus()){
throw new DisabledAccountException("用户登录状态为不禁用状态,请联系系统管理员");
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),getName());
System.out.println("【MyRealm登陆认证】:执行认证逻辑结束");
return simpleAuthenticationInfo;
}
public static void main(String[] args) {
//算出盐值
String credentials="123456";
String salt="10086";
String hashAlgorithmName="MD5";
int hashIterations=1024;
SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
System.out.println(simpleHash);
}
}