虽然,直接用Spring Security和SpringBoot 进行“全家桶式”的合作是最好不过的,但现实总是欺负我们这些没办法决定架构类型的娃子。

Apache Shiro 也有其特殊之处滴。若需了解,可以转战到[Apache Shiro 简介]

1. 添加Shiro依赖

shiro的版本,看个人喜好哈,本文的版本为:

<shiro.version>1.3.2</shiro.version>
复制代码
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-aspectj</artifactId><version>${shiro.version}</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-quartz</artifactId><version>${shiro.version}</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version>
</dependency>
复制代码

2. shiroRealm

授权认证具体实现之地。通过继承 AuthorizingRealm 进而实现,对登录时的账号密码校验功能

@Slf4j
public class ShiroRealm extends AuthorizingRealm {@Autowiredprivate ShiroPermissionRepository shiroPermissionRepository;/*** 授权** @param principalCollection 主要信息* @return 授权信息*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {if (log.isInfoEnabled()){log.info("Authorization begin");}String name= (String) principalCollection.getPrimaryPrincipal();List<String> role = shiroPermissionRepository.queryRoleByName(name);if (role.isEmpty()){SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();simpleAuthorizationInfo.addRoles(role);return simpleAuthorizationInfo;}return null;}/*** 认证** @param authenticationToken 认证token* @return 认证结果* @throws AuthenticationException 认证异常*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {if (log.isInfoEnabled()){log.info("Authentication begin");}UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;Object principal =token.getPrincipal();Object credentials = token.getCredentials();//校验用户名checkBlank(principal,"用户名不能为空");//校验密码checkBlank(credentials,"密码不能为空");//校验姓名String username = (String) principal;UserPO userPO = shiroPermissionRepository.findAllByName(username);if (userPO == null){throw new AccountException("用户名错误");}//校验密码String password = (String) credentials;if (!StringUtils.equals(password,userPO.getPassword())){throw new AccountException("密码错误");}return new SimpleAuthenticationInfo(principal, password, getName());}private void checkBlank(Object obj,String message){if (obj instanceof String){if (StringUtils.isBlank((String) obj)){throw new AccountException(message);}}else if (obj == null){throw new AccountException(message);}}
}
复制代码

3. 配置ShiroConfig

将ShiroConfig、SecurityManager、ShiroFilterFactoryBean交给Spring管理.

  • ShiroRealm: 则上述所描述的ShiroRealm
  • SecurityManager: 管理 所有用户 的安全操作
  • ShiroFilterFactoryBean: 配置Shiro的过滤器
@Configuration
public class ShiroConfig {private final static String AUTHC_STR = "authc";private final static String ANON_STR = "anon";/*** 验证授权、认证** @return shiroRealm 授权认证*/@Beanpublic ShiroRealm shiroRealm(){return new ShiroRealm();}/*** session manager** @param shiroRealm  授权认证* @return  安全管理*/@Bean@ConditionalOnClass(ShiroRealm.class)public SecurityManager securityManager(ShiroRealm shiroRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(shiroRealm);return securityManager;}/*** Filter工厂,设置对应的过滤条件和跳转条件** @param securityManager session 管理* @return shiro 过滤工厂*/@Bean@ConditionalOnClass(value = {SecurityManager.class})public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);shiroFilterFactoryBean.setFilters(filterMap);//URI过滤Map<String,String> map = Maps.newLinkedHashMap();//可过滤的接口路径//所有API路径进行校验map.put("/api/**",AUTHC_STR);shiroFilterFactoryBean.setFilterChainDefinitionMap(map);return shiroFilterFactoryBean;}
}
复制代码

3.1 Shiro 过滤器小插曲

shiro和security也有相似之处,都有自己的 filter chain。翻一番Shiro的源码,追溯一下。发下以下:

3.1.1 ShiroFilterFactoryBean——createFilterChainManager

  protected FilterChainManager createFilterChainManager() {DefaultFilterChainManager manager = new DefaultFilterChainManager();Map<String, Filter> defaultFilters = manager.getFilters();//apply global settings if necessary:for (Filter filter : defaultFilters.values()) {applyGlobalPropertiesIfNecessary(filter);}//Apply the acquired and/or configured filters:Map<String, Filter> filters = getFilters();if (!CollectionUtils.isEmpty(filters)) {for (Map.Entry<String, Filter> entry : filters.entrySet()) {String name = entry.getKey();Filter filter = entry.getValue();applyGlobalPropertiesIfNecessary(filter);if (filter instanceof Nameable) {((Nameable) filter).setName(name);}//'init' argument is false, since Spring-configured filters should be initialized//in Spring (i.e. 'init-method=blah') or implement InitializingBean:manager.addFilter(name, filter, false);}}//build up the chains:Map<String, String> chains = getFilterChainDefinitionMap();if (!CollectionUtils.isEmpty(chains)) {for (Map.Entry<String, String> entry : chains.entrySet()) {String url = entry.getKey();String chainDefinition = entry.getValue();manager.createChain(url, chainDefinition);}}return manager;}
复制代码

从源码可以发现,shiro的过滤器链,添加顺序是:

  1. defaultFilters: shiro默认的过滤器链
  2. filters: 咱们自定义的过滤器链
  3. chains:明确指定要过滤的

3.1.2 DefaultFilterChainManager —— addDefaultFilters

这里咱看看DefaultFilterChainManager 到底添加了那些默认过滤器链,可以看到主要的是:DefaultFilter

protected void addDefaultFilters(boolean init) {for (DefaultFilter defaultFilter : DefaultFilter.values()) {addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);}
}
复制代码

3.1.3 DefaultFilter

anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
复制代码

4. 测它

由于设置对全局接口进行校验,因此,预期结果就是不能够访问啦

map.put("/api/**",AUTHC_STR);
复制代码

4.1 IDAL

@RestController
@RequestMapping( SYSTEM_API +"shiro")
public class ShiroIdal {@Resourceprivate IShiroService iShiroService;@GetMappingpublic HttpEntity obtain(@RequestParam String name){return iShiroService.obtainUserByName(name);}
}
复制代码

4.2 service

@Slf4j
@Service
public class ShiroServiceImpl implements IShiroService {@Resourceprivate ShiroPermissionRepository shiroPermissionRepository;public HttpEntity obtainUserByName(String name) {UserPO userPO = shiroPermissionRepository.findAllByName(name);return HttpResponseSupport.success(userPO);}
}
复制代码

4.3 被劫持的情况

若没 login.jsp,则会直接报错,个人觉得太不和谐了,毕竟现在都是前后端分离的。

4.4 设置允许访问

在URI过滤Map加入以下:

map.put("/api/shiro",ANON_STR);
复制代码

注意: 要在“全局Api劫持”前添加。而且不要使用“HashMap”,为什么?

4.4.1 HashMap

在说为什么前,先了解HashMap这货是什么原理先。

for (Entry<String, String> entry : hashMap.entrySet()) {MessageFormat.format("{0}={1}",entry.getKey(),entry.getValue());
}
复制代码

HashMap散列图是按“有利于随机查找的散列(hash)的顺序”。并非按输入顺序。遍历时只能全部输出,而没有顺序。甚至可以rehash()重新散列,来获得更利于随机存取的内部顺序。

这会影响shiro哪里呢?

Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {for (Map.Entry<String, String> entry : chains.entrySet()) {String url = entry.getKey();String chainDefinition = entry.getValue();manager.createChain(url, chainDefinition);}
}
复制代码

ShiroFilterFactoryBean 中,在构建shiro的filter chain时,会对我们配置的FilterChainDefinitionMap 进行一次遍历,并且将其添加到DefaultFilterChainManager中。

设想以下,若“全局API劫持”在最前面,那么只要在/api/*裆下的,都早早被劫持了。轮得到配置的 anon 么?若由于HashMap的散列排序导致“全局API劫持”在最前面,emmmm,那玩锤子。

4.4.2 LinkedHashMap

因此,建议使用LinkedHashMap,为啥子?撸源码

   static class Entry<K,V> extends HashMap.Node<K,V> {Entry<K,V> before, after;Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}transient LinkedHashMap.Entry<K,V> head;transient LinkedHashMap.Entry<K,V> tail;
复制代码

内部类中多了两个Entry,一个记录前方entry,一个记录后方entry,这样的双向链表结构保证了插入顺序的有序。

LinkedHashMap底层是数组加单项链表加双向链表

  • 数组加单向链表就是HashMap的结构,记录数据用,
  • 双向链表,存储插入顺序用。

有点跑偏了,这些大伙肯定都知道滴了......

转载于:https://juejin.im/post/5ce51e735188253051636dfb

Spring Boot 整合 Shiro相关推荐

  1. Spring Boot整合Shiro + Springboot +vue

    目录 02 Spring Boot整合Shiro p1.shiro概述 1 什么是Shiro 2 Shiro核心组件 p2.Shiro实现登录认证 AccountRealm.java QueryWra ...

  2. Spring Boot 整合 shiro 之盐值加密认证详解(六)

    Spring Boot 整合 shiro 之盐值加密认证详解 概述 不加盐认证 加入密码认证核心代码 修改 CustomRealm 新增获取密文的方法 修改 doGetAuthenticationIn ...

  3. Spring Boot 整合 Shiro(三)Kaptcha验证码 附源码

    前言 本文是根据上篇<Spring Boot 整合Shiro(二)加密登录与密码加盐处理>进行修改,如有不明白的转上篇文章了解. 1.导入依赖 <!-- https://mvnrep ...

  4. 六、Spring Boot整合Shiro

    六.Spring Boot整合Shiro 6.1.整合思路 6.2.创建spring boot项目 6.3.引入shiro依赖 6.4.配置shiro环境 创建配置类ShiroConfig 1.配置: ...

  5. Spring Boot整合Shiro + JSP教程(用户认证,权限管理,图片验证码)

    在此首先感谢**编程不良人**up主提供的视频教程 代码都是跟着up的视频敲的,遇到的一些问题也是通过CSDN博主提供的教程解决的,在此也感谢那些提供bug解决方案的前辈们~ 项目完整代码已经发布到g ...

  6. spring boot整合shiro继承redis_Springboot+Shiro+redis整合

    1.Shiro是Apache下的一个开源项目,我们称之为Apache Shiro.它是一个很易用与Java项目的的安全框架,提供了认证.授权.加密.会话管理,与spring Security 一样都是 ...

  7. spring boot整合shiro+jjwt

    前言 本篇文章将教大家在 shiro + springBoot 的基础上整合 JJWT (JSON Web Token) JWT JSON Web Token(JWT)是一个非常轻巧的规范.这个规范允 ...

  8. spring boot整合Shiro实现单点登录

    默认情况下,Shiro已经为我们实现了和Cas的集成,我们加入集成的一些配置就ok了 1.加入shiro-cas包 <!-- shiro整合cas单点 --><dependency& ...

  9. spring boot整合shiro继承redis_spring-boot-plus集成Shiro+JWT权限管理

    SpringBoot+Shiro+JWT权限管理 Shiro Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 使用Shiro的易于理解的API,您可以 ...

最新文章

  1. DNS Serv管理与维护
  2. msoffice二级各题题型及其分值
  3. JQuery源码-------JQuery中数值型变量的判断isNumeric
  4. Hibernate_13_QBC查询
  5. www服务器把信息组织成,管理信息系统第4章习题.doc
  6. python编辑程序模型_Python编程语言实行尽可能成熟、稳定的新管理模型
  7. 【Python CheckiO 题解】Bird Language
  8. (前端)html与css css 15、标准文档流
  9. 聊聊Spring Data Auditable接口的变化
  10. win11检测不到第二屏幕怎么办 windows11检测不到第二屏幕的解决方法
  11. DDL——数据定义语言
  12. mysql视图子查询_MySQL:FROM子句限制中的带有子查询的视图
  13. linux rzsz 安装包,安装rzsz软件包全攻略
  14. 手摸手深入理解JVM虚拟机--在MacOS系统上编译OpenJDK12并使用CLion调试靠谱教程
  15. 计算机桌面设置上时间表,如何在电脑桌面便签上设置日程安排表?
  16. 网站建设中做到需求分析细致,网站优化也就顺理成章了
  17. bootstrap之双日历时间段选择控件—daterangepicker
  18. iOS9获取手机序列号serialNumber(UDID)
  19. 游戏《一战封神》副本星宿神殿挑战攻略
  20. 商业周刊:苹果新CEO需打造强有力管理团队

热门文章

  1. 新鲜出炉的电信诈骗经历
  2. 无线安全入门、芯片选型、扫描器使用
  3. 从 RequireJS 到 SeaJS(2)
  4. 浏览器还 data URI协议
  5. 取得Access自增标识字段在插入数据后的id值
  6. 转载: 快速理解Docker - 容器级虚拟化解决方案
  7. Python-读取文件例子:一个获取指定目录下一定格式的文件名称和文件修改时间并保存为文件的python脚本 ....
  8. CentOS VS Ubuntu,谁才是更好的 Linux 版本?
  9. cefsharp 网页打印不好用_2019年成人高考打印准考证常见问题解答
  10. 不会自动更新了_手机老是自动更新系统?不想频繁更新,不同手机怎么设置好?...