Apache Shiro
一. 什么是Apache Shiro?
shiro 是基于java的开源的安全管理框架,可以完成认证,授权,会话管理,加密和缓存功能。
二. 为什么要学习 Shiro?
在java 世界中,安全框架有 spring security ,他要依赖于spring,并且比较复杂。shiro比较简单,并且独立。既可以在javase中使用,还可以在javaee中使用,也可以在分布式环境下使用。
三. shiro的结构体系:
Primary Concerns 主要的内容,--------shiro的四大基石。
- Authentication
认证。验证用户的合法性行为,也就是登录认证。 - Authorization
授权 。 授予谁具有访问某些资源的权限。 - Session Manangement
会话管理。用户登录后的用户信息通过session Management来管理。 - Cryptography
加密 。它为我们提供了一些常见的加密算法。并且使用很便捷,实现了数据安全性
Supporting Features --------shiro支持的特性
-
web Support
对web应用程序支持,可以很方便的集成web应用程序。 -
Caching
shiro提供了缓存的支持,支持多种缓存,如:ehcache,redis等 -
Concurrenty
并发支持,支持多个线程并发访问。 -
Test
测试。 -
Run As
支持一个用户在允许的前提下使用另外一个身份登录访问。 -
Remember me
记住我
四. shiro的架构:
-
Subject 主体:
一般指用户,subject用来获取主体信息,通过Principals和
Credentials。 -
Security Manager 安全管理器
安全管理器是shiro的核心。有它来协调管理shiro各个组件的工作。 -
Authenticator 认证管理器
负责验证用户身份 -
Authorizer 授权管理器
负责为合法的用户授权,控制用户可以访问哪些资源。 -
Realms 域
负责与数据库打交道。
五.接下来我们正式说说各个模块的具体的代码实现。
一. Authentication:用户认证
需要提交身份和凭证给shiro。
Principals:用户的身份信息,是subject的标识属性。能够唯一标识subject。如:电话号码,身份证号码,电子邮箱等等。
Credentials:凭证,也就是密码。只被subject知道的秘密值。
Principals和Credentials最常见的组合就是:用户名/密码。在shiro中 通过usernamePasswordToken来指定。 usernamePasswordToken(传入参数就是username,password)
认证流程代码实现:
1.导入依赖:
<!--Shiro核心框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- Shiro使用Srping框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--shiro与整合表现层-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
!-- thymeleaf模板引擎和shiro框架的整合 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>${thymeleaf-extras-shiro.version}</version>
</dependency>
2.编写代码:
2.1 首先先一个shiro配置类
安全管理器(SecurityManager ),会调用认证管理器(Authenticator ),认证管理器里面有认证策略,会调用自定义realm,通过realm来读取数据库中的用户信息,shiro框架会自动跟subject提交登录时传入的token进行比对。实现认证。
/**
* Created by Zrj on
*
* @DATE: 2020/8/29
* @TIME: 18:09
*/
@Configuration
public class ShiroConfig {
//因为shiro核心是安全管理器,所以首先要配置一个安全管理器,来管理各个组件的执行
//(认证器,授权器,加密器,会话管理器),然后定义realm将它设置进去。
//最后需要将安全管理器加入到shiro中去。也就是配置一个filter
@Bean
public MyRealm MyRealm(){
return new MyRealm();
}
//首先要配置一个安全管理器,来管理各个组件的执行(认证器,授权器,加密器,会话管理器)
@Bean
public SecurityManager SecurityManager(MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
/**
* Shiro过滤器的配置
* 这里可以配置认证规则:
* anon:无需认证
* authc: 需要认证
* 授权规则:
* perms:必须拥有某个权限才能访问。
* role:必须拥有某个角色才能访问。
* rest:必须是restful风格的请求,post,get,delete,put
* ssl:必须是安全的请求,即https访问。
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//shiro的核心安全接口,需要把它设置进去,进行shiro的管理
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* AOP式方法级权限检查
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
/**
* 开启Shiro注解通知器
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* thymeleaf模板引擎和shiro框架的整合
*/
@Bean
public ShiroDialect shiroDialect()
{
return new ShiroDialect();
}
}
2.2 自定义realm
//需要继承 AuthorizingRealm类。并实现俩个方法
public class MyRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken)token;
String username = usernamePasswordToken.getUsername();
String password = String.valueOf(usernamePasswordToken.getPassword());
User user= userService.login(username,password);
if (user!=null){
return new SimpleAuthenticationInfo(username,password,getName());
}
throw new UnknownAccountException("认证失败");
}
}
2.3 编写登录类
@RequestMapping("/doLogin")
@ResponseBody
public AjaxResult login(String username, String password){
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return AjaxResult.success();
}catch (Exception e){
e.getMessage();
}
return AjaxResult.fail("用户名或者密码错误");
}
这里我想说明一下:身份和凭证会放到usernamePasswordToken里面去,
在自定义realm中的认证方法参数就能拿到该token信息。
在身份认证过程中,往往会涉及到加密,shiro内部实现了很多的加密算法。如:MD5,SHA等。并且提供了加盐功能。
MD5演示:
**
* shiro中提供的md5加密算法
*/
public class MD5Test {
public static void main(String[] args) {
//只对密码加密
Md5Hash md5Hash = new Md5Hash("1111");
//加盐加密
Md5Hash md5Hash1 = new Md5Hash("1111","zrj");
//加盐加散列次数加密
Md5Hash md5Hash2 = new Md5Hash("1111","zrj",1024);
SimpleHash shash=new SimpleHash("md5","1111","zrj",1024);
System.out.println(md5Hash.toString());
System.out.println(md5Hash1.toString());
System.out.println(md5Hash2.toString());
System.out.println(shash.toString());
}
一. Authorization:授权
权限粒度:
-
粗粒度和细粒度
shiro一般管理的是粗粒度的权限。比如:(菜单,按钮,url)
一般细粒度的权限是通过业务来控制的。 -
权限的表示规则:
资源:操作:实例。 可以用通配符表示
例如:user:add 表示对user有添加的权限。
user:* 表示对user有所有操作的权限。
这种就是细粒度的操作了-- user:delete:100 表示对user表示的第100条记录有删除权限。
代码实现:
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user= (User) SecurityUtils.getSubject().getPrincipal();
//给角色资源进行授权对象
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
// 角色列表
Set<String> roles = new HashSet<String>();
// 功能列表
Set<String> menus = new HashSet<String>();
if (user.getId()!=null && 1L ==user.getId()){ //判断是否为管理员,管理员拥有所有权限
info.addRole("超级管理员");
info.addStringPermission("*:*:*");
}else { //否则的话,根据用户对应的角色,角色对应的权限,查询出来放到授权对象中
roles = userService.selectRoleByUserId(user.getId());
menus=menuService.selectPremsByUserId(user.getId());
//角色加入授权对象
info.addRoles(roles);
//权限加入授权对象
info.addStringPermissions(menus);
}
return info;
}
前台页面就可以根据
标签shiro:hasPermission=“” ,或者shiro:hasRole=“”等等进行按钮以及菜单的权限控制。
后台可以通过,
shiro注解控制到方法级别。例如: @RequiresPermissions("")等等,如果没有对应的权限无法访问。