Shiro框架
源码地址https://gitee.com/lin8081/LWH09
1.Shiro简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
2.Shiro功能
1.主要功能
shiro主要有三大功能模块:
-
Subject:主体,一般指用户。
-
SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
-
Realms:用于进行权限信息的验证,一般需要自己实现。
2.细分功能
-
Authentication:身份认证/登录(账号密码验证)。
-
Authorization:授权,即角色或者权限验证。
-
Session Manager:会话管理,用户登录后的session相关管理。
-
Cryptography:加密,密码加密等。
-
Web Support:Web支持,集成Web环境。
-
Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
-
Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
-
Testing:测试支持;
-
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
-
Remember Me:记住我,登录后,下次再来的话不用登录了。
3.涉及表设计
shiro开发中,涉及到用户,角色,权限,所以需设计如下表:
1.t_user
用户表
2.t_role
角色表
3.t_permission
权限表
4.t_user_role
用户-角色关联表
5.t_role_permission
角色-权限关联表
具体结构查看源代码
4.SpringBoot整合Shiro(例子)
1.spring和shiro的整合依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
2.登录方法
认证:身份认证/登录,验证用户是不是拥有相应的身份。基于shiro的认证,shiro需要采集到用户登录数据使用
subject的login方法进入realm完成认证工作。
@ResponseBody
@PostMapping("/login")
public Map<String,Object> login( @Valid User user){
Map<String,Object> map=new HashMap<String,Object>();
try{
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken uptoken = new
UsernamePasswordToken(user.getUserName(),user.getPassword());
// 执行完subject.login方法,接下来调用自定义realm的认证方法
subject.login(uptoken);
map.put("success", true);
return map;
}catch(Exception e){
e.printStackTrace();
map.put("errorInfo", "用户名或者密码错误!");
return map;
}
}
3.自定义realm
Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源
/**
* 自定义的realm
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 授权方法
* 操作的时候,判断用户是否具有响应的权限
* 先认证 -- 安全数据
* 再授权 -- 根据安全数据获取用户具有的所有操作权限
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1.获取登录的用户数据
User user = (User) principalCollection.getPrimaryPrincipal();//得到唯一的安全数据
//2.根据用户数据获取用户的权限信息(所有角色,所有权限)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<>();//所有角色
Set<String> perms = new HashSet<>();//所有权限
//根据用户信息查找对应的角色
List<Role> roleList = userService.findRoleByUser(user.getUserName());
// 根据获取用户的角色进行赋值,该用户可能有多个角色,需进行遍历
for (Role role : roleList) {
// 添加角色
roles.add(role.getName());
//根据用户信息查找对应的角色
List<Permission> permsList = userService.findPermByUser(role.getId());
// 该角色可能有多个权限,需进行遍历
for (Permission perm : permsList) {
// 添加权限
perms.add(perm.getCode());
}
}
// 设置该用户拥有的权限
info.setStringPermissions(perms);
// 设置该用户拥有的角色
info.setRoles(roles);
return info;
}
/**
* 认证方法
* 参数:传递的用户名密码
*/
// 该方法主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.获取登录的用户名密码(token)
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
String username = upToken.getUsername();
String password = new String( upToken.getPassword());
//2.根据用户名查询数据库
User user = userService.findByName(username);
//3.判断用户是否存在或者密码是否一致
if(user != null && user.getPassword().equals(password)) {
//4.如果一致返回安全数据
//构造方法:安全数据,密码,realm域名
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
//5.不一致,返回null(抛出异常)
return null;
}
}
4.Shiro的配置
SecurityManager 是 Shiro 架构的心脏,用于协调内部的多个组件完成全部认证授权的过程。例如通过调用realm
完成认证与登录。使用基于springboot的配置方式完成SecurityManager,Realm的装配
@Configuration
public class ShiroConfiguration {
//1.创建realm
@Bean
public CustomRealm getRealm() {
return new CustomRealm();
}
//2.创建安全管理器,权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager getSecurityManager(CustomRealm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
//3.配置shiro的过滤器工厂
/**
* 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
*
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
//1.创建过滤器工厂
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
//2.设置安全管理器
filterFactory.setSecurityManager(securityManager);
//3.通用配置(跳转登录页面,为授权跳转的页面)
filterFactory.setLoginUrl("/pages/login.html");//跳转url地址
// 登录成功后登录页
filterFactory.setSuccessUrl("/pages/main.html");
//未授权的url
filterFactory.setUnauthorizedUrl("/pages/error.html");
//4.设置过滤器集合
/**
* 设置所有的过滤器:有顺序map
* key = 拦截的url地址
* value = 过滤器类型
*
*/
Map<String,String> filterMap = new LinkedHashMap<>();
//具有某中权限才能访问
//使用过滤器的形式配置请求地址的依赖权限
//filterMap.put("/mdCheliang/addCheliang","perms[user-add]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址
//使用过滤器的形式配置请求地址的依赖角色
filterMap.put("/pages/main.html","roles[系统管理员]");
// anon:表示可以匿名使用。 authc:表示需要认证(登录)才能使用,没有参数
// 配置不会被拦截的接口,不拦截的要在拦截的上面,依次执行
filterMap.put("/user/**", "anon");
filterMap.put("/kaptcha/**", "anon");
// 静态资源的处理
filterMap.put("/assets/**", "anon");
filterMap.put("/static/**", "anon");
filterMap.put("/**/*.js", "anon");
filterMap.put("/**/*.css", "anon");
//对所有用户认证
filterMap.put("/**", "authc");//当前请求地址必须认证之后可以访问
filterFactory.setFilterChainDefinitionMap(filterMap);
return filterFactory;
}
//开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
4.1shiro配置中的过滤器
例如:filterMap.put("/user/**", “anon”);的anon,无需认证即可访问
Filter关键字 | 解释 |
---|---|
anon | 无参,开放权限,可以理解为匿名用户或游客 |
authc | 无参,需要认证 |
logout | 无参,注销,执行后会直接跳转到 shiroFilterFactoryBean.setLoginUrl(); 设置的 url |
authcBasic | 无参,表示 httpBasic 认证 |
user | 无参,表示必须存在用户,当登入操作时不做检查 |
ssl | 无参,表示安全的URL请求,协议为 https |
perms[user] | 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过 |
roles[admin] | 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”], 当有多个参数时必须每个参数都通过才算通过 |
rest[user] | 根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等 |
port[8081] | 当请求的URL端口不是8081时,跳转到当前访问主机HOST的8081端口 |
注意:anon, authc, authcBasic, user 是第一组认证过滤器,perms, port, rest, roles, ssl 是第二组授权过滤
器,要通过授权过滤器,就先要完成登陆认证操作(即先要完成认证才能前去寻找授权) 才能走第二组授权器
(例如访问需要 roles 权限的 url,如果还没有登陆的话,会直接跳转到
shiroFilterFactoryBean.setLoginUrl(); 设置的 url )
4.2基于注解的授权
(1)RequiresPermissions
配置到方法上,表明执行此方法必须具有指定的权限
//查询
@RequiresPermissions(value = "user-find")
public String find() {
return "查询用户成功";
}
(2)RequiresRoles
配置到方法上,表明执行此方法必须具有指定的角色
//查询
@RequiresRoles(value = "系统管理员")
public String find() {
return "查询用户成功";
}
注意:基于注解的配置方式进行授权,一旦操作用户不具备操作权限,目标方法不会被执行,而且会抛出
AuthorizationException 异常。所以需要做好统一异常处理完成未授权处理
shiro的步骤流程