springboot2 集成shiro-spring-boot-web-starter
目录
- 1.引包
- 2. 配置shiro
- 2.1 配置类
- 2.2 登录页配置
- 2.3 shiro默认过滤器
- 2.4 自定义Reaml类
- 3 用户登录
- 3.1 页面设计
- 3.2 登录处理
- 3.3 密码匹配原理
- 4. 权限管理
- 4.1 url和注解组合使用
- 4.2 @RequireXXX注解可能的Bug
- 4.3 注解权限
shiro是web开发中常用的使用安全管理框架,通过shiro-spring-boot-web-starter方式集成Shiro到springboot2可以简化配置。
1.引包
maven方式在项目pom.xml中引入shiro starter包的坐标,这里引用了1.4.1版本
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.4.1</version></dependency>
2. 配置shiro
starter已经做了很多自动配置工作,具体可以参考ShiroAutoConfiguration.java、ShiroBeanAutoConfiguration.java和ShiroWebAutoConfiguration.java这几个文件。
2.1 配置类
这里使用新建shiroConfig.java类方式进行shiro配置。主要配置Realm、url过滤器、密码匹配器和安全管理器这几个组件就可以让shiro正常工作。
@Configuration
public class shiroConfig {// 配置自定义Realm@Beanpublic UserRealm userRealm() {UserRealm userRealm = new UserRealm();userRealm.setCredentialsMatcher(credentialsMatcher()); //配置使用哈希密码匹配return userRealm;}// 配置url过滤器@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition() {DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();chainDefinition.addPathDefinition("/captcha", "anon");chainDefinition.addPathDefinition("/logout","anon");chainDefinition.addPathDefinition("/layuiadmin/**", "anon");chainDefinition.addPathDefinition("/druid/**", "anon");chainDefinition.addPathDefinition("/api/**", "anon");// all other paths require a logged in userchainDefinition.addPathDefinition("/login","anon");chainDefinition.addPathDefinition("/**", "authc");return chainDefinition;}// 设置用于匹配密码的CredentialsMatcher@Beanpublic HashedCredentialsMatcher credentialsMatcher() {HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME); // 散列算法,这里使用更安全的sha256算法credentialsMatcher.setStoredCredentialsHexEncoded(false); // 数据库存储的密码字段使用HEX还是BASE64方式加密credentialsMatcher.setHashIterations(1024); // 散列迭代次数return credentialsMatcher;}// 配置security并设置userReaml,避免xxxx required a bean named 'authorizer' that could not be found.的报错@Beanpublic SessionsSecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(userRealm());return securityManager;}}
2.2 登录页配置
shiro默认的登录页是/login.jsp,需要在项目配置文件application.yml中修改默认登录页等配置。
shiro:loginUrl: /loginsuccessUrl: /
2.3 shiro默认过滤器
shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置过滤指定url的访问权限。
配置缩写
对应的过滤器
功能
anon
AnonymousFilter
指定url可以匿名访问
authc
FormAuthenticationFilter
指定url需要form表单登录,默认会从请求中获取username、password,rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。
authcBasic
BasicHttpAuthenticationFilter
指定url需要basic登录
logout
LogoutFilter
登出过滤器,配置指定url就可以实现退出功能,非常方便
noSessionCreation
NoSessionCreationFilter
禁止创建会话
perms
PermissionsAuthorizationFilter
需要指定权限才能访问
port
PortFilter
需要指定端口才能访问
rest
HttpMethodPermissionFilter
将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释
roles
RolesAuthorizationFilter
需要指定角色才能访问
ssl
SslFilter
需要https请求才能访问
user
UserFilter
需要已登录或“记住我”的用户才能访问
shiro常用的权限控制注解,可以在控制器类上使用
注解
功能
@RequiresGuest
只有游客可以访问
@RequiresAuthentication
需要登录才能访问
@RequiresUser
已登录的用户或“记住我”的用户能访问
@RequiresRoles
已登录的用户需具有指定的角色才能访问
@RequiresPermissions
已登录的用户需具有指定的权限才能访问
2.4 自定义Reaml类
Realm是实现自定义登录和授权的核心类,这里继承了抽象类AuthorizingRealm并重写doGetAuthenticationInfo(用于登录验证)和doGetAuthorizationInfo(用于权限验证)这俩个方法。doGetAuthenticationInfo主要作用是获取用户输入的用户名、密码等信息并从数据库中取出保存的密码交给shiro,由shiro的密码匹配器进行匹配。
/*** 认证 ,用户名密码校验*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {String username = (String) authenticationToken.getPrincipal();User user = userDao.getByUsername(username);if (user == null) {throw new UnknownAccountException(); // 账号不存在}if (user.getStatus() != 0) {throw new LockedAccountException(); // 账号被锁定}String salt = user.getSalt();SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(salt),getName());return authenticationInfo;}
根据用户输入的用户名,在数据库中查找到用户记录,并用查到的用户对象、数据库中存储的密码、密码盐和Realm对象名字构建一个认证信息对象(SimpleAuthenticationInfo)交给系统进行密码验证。
doGetAuthorizationInfo主要是获取用户的角色和权限,并交给Shiro去判断是否具有访问资源的权限。
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {User user = (User) SecurityUtils.getSubject().getPrincipal();SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();// 角色Set<String> roles = new HashSet<>();// 权限// 测试用权限if ("admin".equals(user.getUsername())) {roles.add("admin");permissions.add("op:write");} else {roles.add("user");permissions.add("op:read");}authorizationInfo.setRoles(roles);authorizationInfo.setStringPermissions(permissions);return authorizationInfo;}
支持shiro的配置工作已经基本完成,已经可以正常工作了。接下来就是与前端配合完成页面的登录和权限控制等工作。
3 用户登录
3.1 页面设计
在需要登录的前端 html页面上,向后台登录url提交包含用户名和密码字段的表单。
3.2 登录处理
在后台登录url中,接收用户名密码,据此创建一个usernamePasswordToken令牌,交由Shiro并调用login()方法进行登录,如果不抛出任何异常表明登录成功,如果抛出异常,这根据异常种类返回提示出错信息给用户。
@ResponseBody@PostMapping("login")public JsonResult doLogin(String username, String password, String vercode, HttpServletRequest request) {if ("".equals(username.trim()) || "".equals(password.trim())) {return JsonResult.error("账号或密码不能为空");}if (!CaptchaUtil.ver(vercode, request)) {//CaptchaUtil.clear(request); // 清除session中的验证码return JsonResult.error("验证码不正确");}try {UsernamePasswordToken token = new UsernamePasswordToken(username, password);SecurityUtils.getSubject().login(token);//addLoginRecord(getLoginUserId(), request); // 记录登录信息HashMap<String, String> map = new HashMap<>();map.put("access_token", "1111111111111111111"); // 模拟登录令牌return JsonResult.ok("登录成功").put("data", map);} catch (IncorrectCredentialsException ice) {return JsonResult.error("密码错误");} catch (UnknownAccountException uae) {return JsonResult.error("账号不存在");} catch (LockedAccountException e) {return JsonResult.error("账号被锁定");} catch (ExcessiveAttemptsException eae) {return JsonResult.error("操作频繁,请稍后再试");}}
验证码使用的是 EasyCaptcha工具
3.3 密码匹配原理
在登录后台url中,我们拿到用户输入的用户名和密码,并组成一个token传给了shiro的login()方法。
login()方法进行登录验证过程中,调用Realm对象的doGetAuthenticationInfo()方法,在这里根据token中的用户名在数据库中查找对应用户,用用户对象、数据库中存储的密码、密码盐和Realm对象名字构建一个认证信息对象(SimpleAuthenticationInfo)交给系统进行密码验证。
根据在配置文件中配置的密码匹配器,调用doCredentialsMatch()方法进行密码匹配。默认是SimpleCredentialsMatcher匹配器,他是以明文方式进行用户输入的密码和数据库中保存的密码进行匹配。
@Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {Object tokenHashedCredentials = hashProvidedCredentials(token, info);Object accountCredentials = getCredentials(info);return equals(tokenHashedCredentials, accountCredentials); }
我们在配置中使用的是HashedCredentialsMatcher匹配器,使用更安全的sha256哈希算法,指定了数据库中密码字段使用base64方式加密。
那么在创建用户或修改密码时怎么生成加密密码呢?利用shiro提供的simplehash()方法就可以,如下指定了sha256算法,密码字符串,密码盐和迭代次数(需要和config配置里的次数相同),最后对生成的哈希密码串进行base64编码。
new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, ByteSource.Util.bytes(salt), hashIterations).toBase64();
4. 权限管理
4.1 url和注解组合使用
shiro可以使用url配置控制权限,也可以在控制器类上使用注解控制权限。同时使用两种配置方式灵活结合,才是适应不同应用场景的最佳实践。只用注解或只用url配置,都不够灵活,有时会很麻烦。思路是:
用url配置控制鉴权,实现粗粒度控制;用注解控制授权,实现细粒度控制
4.2 @RequireXXX注解可能的Bug
注意:解决spring aop和注解配置一起使用的bug。如果您在使用shiro注解配置的同时,引入了spring aop的starter,会有一个奇怪的问题,导致shiro注解的请求,不能被映射,需加入以下配置:
@Beanpublic static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();/*** setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。* 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。 加入这项配置能解决这个bug*/defaultAdvisorAutoProxyCreator.setUsePrefix(true);//defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}
4.3 注解权限
控制器上通过注解配置详细的角色和权限,多个权限和角色之间默认是“与”关系,可以通过logical参数设置为“或”。
@RequiresRoles("user")@GetMapping("/user/list.html")public String userList() {return "user/user/list";}@RequiresPermissions("op:read")@GetMapping("user/userform.html")public String userForm() {return "user/user/userform";}@RequiresRoles("admin")@GetMapping("/administrators/list.html")public String adminList() {return "user/administrators/list";}@RequiresPermissions("op:write")@GetMapping("administrators/adminform.html")public String administratorForm() {return "user/administrators/adminform";}@RequiresRoles( value = {"admin", "user"}, logical = Logical.OR)@GetMapping("/administrators/role.html")public String roleList() {return "user/administrators/role";}
springboot2 集成shiro-spring-boot-web-starter相关推荐
- Spring Boot(3)---Spring Boot启动器Starter详解
Spring Boot的启动器Starter详解 Spring Boot 简化了 Spring 应用开发,不需要配置就能运行 Spring 应用, Spring Boot 管理 Spring 容器.第 ...
- Spring Boot Web
一. 概述 下面我们将进入 SpringBoot 基础阶段的学习. 在没有正式的学习 SpringBoot 之前,我们要先来了解下什么是 Spring . 我们可以打开 Spring 的官网 ( ht ...
- PART 5: INTEGRATING SPRING SECURITY WITH SPRING BOOT WEB
转自:http://justinrodenbostel.com/2014/05/30/part-5-integrating-spring-security-with-spring-boot-web/ ...
- okta-spring_通过Okta的单点登录保护Spring Boot Web App的安全
okta-spring "我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多 ...
- 通过Okta的单点登录保护Spring Boot Web App的安全
"我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. 您可以使 ...
- spring boot web 开发示例
一.创建Maven工程 创建maven工程,packaging 类型选择jar. 二.配置相关maven依赖. 1,首先你需要在pom中最上方添加spring boot的父级依赖,这样当前的项目就是S ...
- Spring Boot Web应用程序中注册 Servlet 的方法实例
Spring Boot Web应用程序中注册 Servlet 的方法实例 本文实例工程源代码:https://github.com/KotlinSpringBoot/demo1_add_servlet ...
- spring boot web jar说明
spring boot web jar说明 spring-boot-starter-web: spring-boot-starter:spring核心jar,自动配置支持.日志和YAML spring ...
- 如何部署同一个Spring boot web 应用到不同的环境
在现实项目当中我们往往都有不同的部署环境,例如:dev数据库, system test 数据库 和production 数据库, 那么如何把同一个spring boot web app 部署到不同的数 ...
- Spring boot web(2):web综合开发
1 web开发 Spring boot web 开发非常简单,其中包括常用的 json输出.filters.property.log等 1.1 json接口开发 在以前的Spring 开发我么提供js ...
最新文章
- 如何用Python实现多任务版的udp聊天器
- 使用GitLab或者Github简单实用地将数据导入Colab的方法
- Linux服务器init 5启动图形界面,报错Retrigger failed udev events的解决方法
- Angular1.63 绑定数据与继承
- IOS--CALayer的介绍及使用技巧
- 【环境搭建000】详细图解ubuntu 上安装配置eclips
- SqlServer死锁com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process ID 52) was deadlock
- Spring Boot 多环境配置(properties和yaml方法的比较)
- 阿里云服务器疑似误报异地登录?怎么查看
- JAVA读取NC文件的工具包
- mysql chroot debian_在我的终端提示中“${debian_chroot:+($debian_chroot)}”有什么作用?...
- 一种基于Android、iOS系统的移动端车牌识别方法,实现手机拍照识别车牌
- gcc 中-O -O1 -O2 -O3 -Os -Ofast -Og优化的原理
- B. Shifting Sort (思维)
- Mysql常用逻辑及函数大全
- ubuntu 8.10安装配置经验(Intrepid Ibex)——转载
- 助力 AI 银行发展 网易智企打造一站式金融服务
- 广德现场:夜山明·潮牌酒倾情助阵第七届国际山地自行车开赛!
- ORACLE之ora-01722和ORA-01403的错误测试
- 6、中小企业网络架构-防火墙基本配置
热门文章
- wltp和nedc续航差多少_WLTP续航和NEDC续航差多少?
- 【C++】数据结构——向量
- 怎样用路由器共享需要网页认证的wifi
- Log4j2配置SMTP邮件实现邮件发送
- 2019_WWW_Dual graph attention networks for deep latent representation of multifaceted social effect
- 【解题报告】openjudge Freda的越野跑 数据结构与算法mooc 内排序
- Python 自然语言处理(基于jieba分词和NLTK)
- Json工具Demo(二)
- android 11鼠标右键返回功能(已验证)
- 网上发现的一个 《Flashflex大全》