最近在做项目的时候需要用到shiro做认证和授权来管理资源

在网上看了很多文章,发现大多数都是把官方文档的简介摘抄一段,然后就开始贴代码,告诉你怎么怎么做,怎么怎么做

相信很多小伙伴即使是跟着那些示例代码做完配完,并且成功搭建,估计也是一头雾水,可能会不理解,为什么要这么做

本人也是在看了大量文章之后,然后又自己动手搭了一便,得出如下使用的感悟,特此分享给大家

依照程序,我要在这里对shiro做一些简介,以下内容翻译与官网首页

Apache SHIO是一个功能强大、易于使用的Java安全框架,它执行身份验证、授权、加密和会话管理。有了Shiro易于理解的API,您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的Web和企业应用程序。

其次要和相关类似技术进行一个对比

在spring大家族中,安全框架也是肯定有的,那就是spring-security,至于我们为什么要用shiro而不用spring-security大家可以自行搜索下相关内容我们在此就不过多阐述了

ok,废话少说,我们直奔主题

可能有些小伙伴在用shiro这种框架的时候会有这种疑惑,我们为什么要用这样的框架

首先我们开发者在做一个项目的时候,对资源的权限控制和划分应该是必不可少的一部分,即哪些资源哪些角色可以访问,哪些资源哪些角色不可以访问

那么试想下这种情况,如果没有shiro这种框架,你要实现对资源的权限控制,可能会有如下几点

1,首先你要设计角色

2,为资源分配角色,并且统一注册进配置文件中

4,给用户授权

3,每当有新的请求进来时你要验证当前用户的身份,然后遍历资源权限的配置文件,查明当前用户的角色是否有资格访问

第1,2步体现在设计层面,第3步要周而复始每当有新的请求的时候都要做一遍的事情,这一步可以使用spring提供的aop机制,配置成前置通知

综上所述是不是感觉很麻烦

那么shiro呢,就为我们简化了上述步骤,使我们能以一种较为简洁的方式,实现对项目资源的权限控制

同时shiro还集成了很多加密机制(MD5,base64,SHA)等,可以直接以调用的方式对我们的密码或者报文进行加密措施非常的方便

首先我门引入pom依赖

<!--  shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>

集成shiro非常的简单,我门只需要自定义一下我们的认证和授权然后配置以下过滤器即可

认证和授权

import com.sc.starry_sky.entity.PageData;
import com.sc.starry_sky.service.UserService;
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.authc.UsernamePasswordToken;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import java.util.ArrayList; @Component("MyShiroRealm")
public class MyShiroRealm extends AuthorizingRealm{ //用于用户查询
@Autowired
private UserService userService; //添加角色权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取用户
PageData user = (PageData) principalCollection.getPrimaryPrincipal();
//获取权限列表
ArrayList<PageData> roles = (ArrayList<PageData>) user.get("roles");
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
roles.forEach(item -> simpleAuthorizationInfo.addRole(item.getString("roleName")));
return simpleAuthorizationInfo;
} //登录认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; String username = usernamePasswordToken.getUsername(); PageData user = userService.findUserByUsername(username);
if (user == null) {
return null;
} else {
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getString("password"), getName());
return simpleAuthenticationInfo;
}
} }

自定义密码的校验规则

import com.sc.starry_sky.util.PasswordUtil;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import com.sc.starry_sky.entity.PageData;
import org.springframework.stereotype.Component; @Component("CredentialMatcher")
public class CredentialMatcher extends SimpleCredentialsMatcher{ @Autowired
PasswordUtil passwordUtil; @Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
PageData user = (PageData)info.getPrincipals().getPrimaryPrincipal(); //这里取出的是我们在认证方法中放入的用户信息也就是我们从数据库查询出的用户信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;//这里是前端发送的用户名密码信息
String requestPwd = new String(usernamePasswordToken.getPassword());
String requestPwdEncryption = passwordUtil.getEncryptionPassword(requestPwd, user.getString("userId"));//获取加密后的密码 String dbPwd = user.getString("password");
return requestPwdEncryption.equals(dbPwd);
} }

密码加密工具类

import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import java.util.Random; @Component
public class PasswordUtil { @Value("${password.algorithmName}") //这个可以在application.properites里自行配置 如(md5,sha,base64)等等
private String algorithmName;
@Value("${password.hashIterations}")//这个是加密的次数
private int hashIterations; /**
* 返回加密后的密码
* @param password
* @param userId
* @return
*/
public String getEncryptionPassword(String password , String userId){
      //四个参数的讲解 1,加密类型,2,原始密码,3,盐值,可以是固定的,这里我门采用用户的userId作为用户的盐值进行加盐,4,加密的迭代次数
return new SimpleHash(algorithmName, password, userId, hashIterations).toString();
}
}

配置过滤器

import java.util.HashMap;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class ShiroConfig { @Autowired
MyShiroRealm myShiroRealm;
@Autowired
CredentialMatcher CredentialMatcher; @Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager); filterFactoryBean.setLoginUrl("/loginPage"); // 成功登陆后的界面
//filterFactoryBean.setSuccessUrl("/indexPage"); // 没有权限访问的界面
//filterFactoryBean.setUnauthorizedUrl("/unauthorized"); HashMap<String, String> filterMap = new HashMap<>(); // 配置不会被拦截的链接 顺序判断
filterMap.put("/static/**", "anon");
filterMap.put("/doLogin", "anon");
filterMap.put("/loginPage", "anon");
filterMap.put("/SubmitRegistInformation", "anon");
filterMap.put("/SendMessageCode", "anon");
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterMap.put("/logout", "logout"); // <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterMap.put("/**", "authc");
// 所有的页面都需要user这个角色
filterMap.put("/**", "roles[user]"); filterFactoryBean.setFilterChainDefinitionMap(filterMap);
return filterFactoryBean;
} @Bean("securityManager")
public SecurityManager securityManager(@Qualifier("myShiroRealm") MyShiroRealm authRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
return manager;
} @Bean("myShiroRealm")
public MyShiroRealm myShiroRealm() {
//设置缓存
myShiroRealm.setCacheManager(new MemoryConstrainedCacheManager());
//设置密码校验规则
myShiroRealm.setCredentialsMatcher(CredentialMatcher);
return myShiroRealm;
} }

登录的controller方法

@Controller
public class LoginRegistController extends BaseController { @Autowired
PasswordUtil passwordUtil; @Autowired
UserService userService; @PostMapping("/doLogin")
@ResponseBody
public Map<String,Object> doLogin(){
HashMap<String, Object> resultMap = new HashMap<String, Object>();
try{
PageData user = this.getPageData();
UsernamePasswordToken token = new UsernamePasswordToken(user.getString("username"), user.getString("password"));
SecurityUtils.getSubject().login(token);
PageData db_user = (PageData) SecurityUtils.getSubject().getPrincipal();//登录成功之后,取出用户信息放进session存储域
this.getRequest().getSession().setAttribute("user_information",db_user);
resultMap.put("status", 200);
resultMap.put("message", "登录成功");
return resultMap;
}catch(Exception e){
resultMap.put("status", 500);
resultMap.put("message", e.getMessage());
return resultMap;
}
} @GetMapping("/loginPage")
public ModelAndView loginPage(String name){
ModelAndView mv = new ModelAndView();
mv.setViewName("login_regist");
return mv;
}
}

这样一个基础的shiro就算是搭建完成了,至于其余的高级特性,还需要小伙伴门私下里自行研究了!

如有不明之处,欢迎下方评论指正,感谢您的阅读!

如果你的shiro没学明白,那么应该看看这篇文章,将shiro整合进springboot的更多相关文章

  1. 没学过CSS等前端的我,也想美化一下自己的博客

    随便说几句: 自己一直学的都是 C++和 Java 以及 Python语言,根本不懂高大上的 CSS 和 著名的 HTML5.感觉那些能自己设计那么漂亮的博客的朋友都好厉害.可以自己加上博客公告栏的小 ...

  2. 为什么学完C语言觉得好像没学一般?

    不少同学从Hello world学到文件操作之后,回顾感觉会又不会? 学会了又感觉没学会?这种不踏实.模糊虚无的感觉?   原因在于编程不同于理论学科,你听懂和理解了理论就可以运用. 比如历史地理,看 ...

  3. 又到期末了,为什么学完C语言觉得好像没学一般?复习资料来一份

    不少同学从Hello world学到文件操作之后,回顾感觉会又不会? 学会了又感觉没学会?这种不踏实.模糊虚无的感觉? 原因在于编程不同于理论学科,你听懂和理解了理论就可以运用,比如历史地理,看完书, ...

  4. 我没学过计算机,是怎么接了四个私活还挣了两个 iPad 的?

    你好,我是悟空哥,「7年项目开发经验,全栈工程师,开发组长,超喜欢图解编程底层原理」.我还手写了2个小程序,Java刷题小程序,PMP刷题小程序.我的 GitHub. 前言 大家看到这篇文章的时候,我 ...

  5. log日志重复输出问题(没弄明白原因)

    在别的模块调用定义好的函数 输出的日志出现第一次输出输出一条,第二次输出输出两条...的情况 最后在定义函数处remove了句柄 引用了https://blog.csdn.net/huilan_sam ...

  6. 一步步教你轻松学支持向量机SVM算法之案例篇2

    一步步教你轻松学支持向量机SVM算法之案例篇2 (白宁超 2018年10月22日10:09:07) 摘要:支持向量机即SVM(Support Vector Machine) ,是一种监督学习算法,属于 ...

  7. 一步步教你轻松学支持向量机SVM算法之理论篇1

    一步步教你轻松学支持向量机SVM算法之理论篇1 (白宁超 2018年10月22日10:03:35) 摘要:支持向量机即SVM(Support Vector Machine) ,是一种监督学习算法,属于 ...

  8. JSP还有必要学吗?这篇文章告诉你

    阅读本文大概需要 12.4 分钟. 来源:http://suo.im/4wqRi7 作者:杨明翰 前戏   前后端分离已成为互联网项目开发的业界标准使用方式,通过nginx+tomcat的方式(也可以 ...

  9. 如果你每次面试前都要去背一篇Spring中Bean的生命周期,请看完这篇文章

    前言 当你准备去复习Spring中Bean的生命周期的时候,这个时候你开始上网找资料,很大概率会看到下面这张图: 先不论这张图上是否全面,但是就说这张图吧,你是不是背了又忘,忘了又背? 究其原因在于, ...

随机推荐

  1. daemon_int

    摘自 UNP #include "unp.h" #include <syslog.h> #define MAXFD 64 extern int daemon_proc; ...

  2. GPS欺骗(一)—无人机的劫持

    本文作者:唯念那抹瑞利蓝 今天我们所讲的是GPS欺骗的方式和简单的定义.让大家对GPS欺骗这个方面有所了解.GPS是全世界地一个卫星定位系统,由美国制造. 0×01 例子2011年伊朗劫持美国无人机 ...

  3. Kafka集群副本分配算法解析

    副本分配算法如下: 将所有N Broker和待分配的i个Partition排序. 将第i个Partition分配到第(i mod n)个Broker上. 将第i个Partition的第j个副本分配到第 ...

  4. 【转】JMeter学习参数化User Defined Variables与User Parameters

    偶然发现JMeter中有两个元件(User Defined Variables与User Parameters)很相近,刚开始时我也没注意,两者有什么不同.使用时却发现两者使用场景有些不同,现在小结一 ...

  5. 网络请求 爬虫学习笔记 一 requsets 模块的使用 get请求和post请求初识别,代理,session 和ssl证书

    前情提要: 为了养家糊口,为了爱与正义,为了世界和平, 从新学习一个爬虫技术,做一个爬虫学习博客记录 学习内容来自各大网站,网课,博客. 如果觉得食用不良,你来打我啊 requsets 个人觉得系统自 ...

  6. 深度了解git工具

    今天给大家介绍一个深度了解git的学习视频,视频讲的挺好的,可以听听. 深度了解git学习视频

  7. 多线程编程,CPU是如何解决多线程内存访问问题的

    CPU对内存变量的修改是先读取内存数据到CPU Cache中,然后再由CPU做运算,运算完成后继续写入到内存中 在单核CPU中,这完全没有问题,然而在多核CPU中,每一个CPU核心都拥有自己独立的Ca ...

  8. rabbit服务器挂掉以后,保证队列消息还存在(tp框架)(第三篇)

    上接 第二篇 : http://www.cnblogs.com/spicy/p/7921870.html 第二篇解决了 如果其中一个worker挂掉了啦,如何保证消息不丢掉,并重新分发给其他worke ...

  9. 0day发布网站[转载]

    1.中文版http://www.wooyun.org/index.php2.中文版http://www.sitedirsec.com/3.http://www.sebug.net/ http://ww ...

  10. PHP CURL 伪造IP和来路

    //随机IP function Rand_IP(){ $ip2id= round(rand(, ) / ); //第一种方法,直接生成 $ip3id= round(rand(, ) / ); $ip4 ...