shiro-springboot集成
程序员文章站
2022-03-15 09:54:33
1.shiro-springboot集成的优势: 方便了対用户权限的管理,和対用户权限进行鉴权1.1创建shiro-springboot工程1.2引入依赖 org.springframework.boot spring-boot-starter-parent ...
1.shiro-springboot集成的优势: 方便了対用户权限的管理,和対用户权限进行鉴权
1.1创建shiro-springboot工程
1.2引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot集成Junit 的启动依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--thymeleaf启动依赖,它是一个前端模板引擎,完全替代jsp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<!--引入的是shiro与spring的集成-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--aop 的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- web相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- test相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 阿里巴巴的Druid数据源依赖启动器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- MyBatis依赖启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- MySQL数据库连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
3.创建com.aaa.springboot根目录,并在当前包下面创建启动器MyApplication
1.3注意:MyApplication启动器必须在根目录下创建,否则会有问题
@SpringBootApplication //标记当前类是一个springboot启动类,注意一个应用只有一个启动类;
//注意一个应用只有一个启动类
//启动类要放在根目录,只有两个目录,springboot 应用才会扫描到对应的controller , service ,dao ,entity层里的东西.
public class MyApplication {
public static void main(String[] args) {
//启动springboot应用
SpringApplication.run(MyApplication.class,args);
}
}
1.4 com.aaa.springshiro.realm
package com.aaa.springshiro.realm;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* 认证 + 授权
*/
public class UserRealm extends AuthorizingRealm {
// 获取认证信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
System.out.println("username=="+username);
// 需要自己取数据库校验当前用户名是否存在 如果不存在,用改手动方式抛出
if (!username.equals("root")){
throw new UnknownAccountException();
}
// 根据据用户名 去数据库查询密码
// 假装去数据获取对应用户名的密码
String passwordFromDB = "123456";
// 得到用户 真实的数据哭的用户名 密码认证信息
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,passwordFromDB,this.getName());
//返回给securityMange 进行认证
return authenticationInfo;
}
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
System.out.println("username:"+username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 去数据库查询 root 相关的权限 和 角色
if (username.endsWith("root")){
// 设置当前用户角色
authorizationInfo.addRole("boss");
// 给当前用户设置权限
authorizationInfo.addStringPermission("goods:add");
authorizationInfo.addStringPermission("goods:delete");
authorizationInfo.addStringPermission("user:delete");
}
// 返回个securityManager 当前用户的角色 和权限
return authorizationInfo;
}
}
1.5 com.aaa.springshiro.config
package com.aaa.springshiro.config;
import com.aaa.springshiro.realm.UserRealm;
import org.apache.shiro.authz.annotation.*;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* ClassName:ShiroConfig
* Package:com.aaa.springshiro.config
* Description:
*
* @date:2020/7/29 18:53
* @author:lee907463383@qq.com
*/
@Configuration //表明当前是一个配置类 相当于 bean.xml文件
public class ShiroConfig {
@Bean //1.将创建的UserRealm 加入到容器
public UserRealm getUserRealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
@Bean //2.将SecurityManager加入容器, 把UserRealm 和 SecurityManager绑定到一起快
// 当容器创建SecurityManager 时, 会去 容器中查找一个UserRealm类型的bean
public SecurityManager getSecurityManager(UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean //3.设置shiro 过滤器:作用就是过滤拦截,鉴权当前用户是否有 对应的角色和权限
//把ShiroFilterFactoryBean加入容器,并且与SecurityManager绑定到一起
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//将setSecurityManager与拦截器进行绑定
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 没有登录,当访问时要跳转到登录页面
shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
//登陆成功, 自动跳转到主页面;
shiroFilterFactoryBean.setSuccessUrl("/user/toIndex");
//当登录后, 但是权限不足的时候, 会自动跳转到该页面.
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");
//Map对象集合 (key,value)
//自定义一个Map<String, String>设置
Map<String, String> filterChainDefinitionMap = new HashMap<>();
/*anon表示配置不被拦截的连接 顺序判断 */
// 设置当前路径 不被shiro 拦截
filterChainDefinitionMap.put("/user/login","anon");
// 配置游客路径 不拦截 无论是否登陆 都可以访问
filterChainDefinitionMap.put("/guest/*","anon");
// 访问当前路径 roles[root]当前必须拥有root角色 , roles[root,manager] 当前用户必须拥有 列那个角色
/*filterChainDefinitionMap.put("/user/add","roles[boss]");*/
filterChainDefinitionMap.put("/user/add","roles[root]");
// 访问当前路径 必须拥有 user:delete
filterChainDefinitionMap.put("/user/delete","perms[user:delete]");
// 拦截所有路径 ***** 一定要写在再最后
filterChainDefinitionMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean //基于注解的权限 鉴权 也就是说不通过上边复杂的方式进行 权限的限定
// @RequiresGuest,@RequiresRoles(),@RequiresUser,@RequiresPermissions()@RequiresAuthentication
//通知Advisor AOP.相当于<!-- 开启aop asjpectj 功能-->
//<aop:aspectj-autoproxy/>
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
// //将setSecurityManager与拦截器进行绑定
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourceAdvisor;
}
@Bean// <aop:aspectj-autoproxy/> 开启aop 注解,像容器注入一个 DefaultAdvisorAutoProxyCreator
@ConditionalOnMissingBean
DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =
new DefaultAdvisorAutoProxyCreator();
// 使用cglib 完成代理
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/*这里的异常是从前台抓取的,你要记得你是后台,你要学会从后台抓取,如何抓? 这是个提升*/
@Bean //当前异常处理的就是 shiro 基于注解鉴权时, 不满足权限时需要抓住异常,因为不是所有的都会自动去403页面
//比如:GoodsController,FirstController中注解引起的错误,需要抓取异常;
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver =
new SimpleMappingExceptionResolver();
//Properties 就是读取配置文件的键対值
Properties property = new Properties();
//key 就是要捕捉异常的 全限定名
property.setProperty("org.apache.shiro.authz.AuthorizationException","/403.html");
resolver.setExceptionMappings(property);
return resolver;
}
}
1.6 com.aaa.springshiro.controller.UserController
package com.aaa.springshiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* ClassName:UserController
* Package:com.aaa.springshiro.controller
* Description:
*
* @date:2020/7/29 19:15
* @author:lee907463383@qq.com
*/
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/toIndex")
public String toIndex(){
return "index";
}
@RequestMapping("/login")
public String login(String username,String password){
Subject subject = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken(username,password);
subject.login(token);
if (subject.isAuthenticated()){
return "index";
}else {
return "login";
}
}
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()){
subject.logout();
}
return "login";
}
@RequestMapping("/unauthorized")
public String unauthorized(){
return "403";
}
@RequestMapping("/add")
public String userAdd(){
return "addUser";
}
@RequestMapping("/delete")
public String userdelete(){
return "deleteUser";
}
}
1.7src/main/resources
403.html <h2>无访问权限</h2>
addgoods.html <h2>addgoods</h2>
addUser.html <h2>addUser</h2>
deletegoods.html <h2>deletegoods</h2>
deleteUser.html <h2>deleteUser</h2>
goodslist.html <h2>显示所有商品</h2>
index.html
<a href="/user/add">新增</a>
<a href="/user/logout">退出</a>
<div>
<a href="/goods/getGoodsList">显示所有商品 </a>
<a href="/goods/addGoods">添加商品 </a>
<a href="/goods/deleteGoods">删除商品 </a>
</div>
<a href="/user/delete">删除</a>
<a href="/guest/first2">游客</a>
login.html
<form action="/user/login" method="get">
账号: <input type="text" name="username">
密码: <input type="password" name="password">
<input type="submit"value="登录">
</form>
<a href="/guest/first2">游客</a>
message.html <h2>message</h2>
2.如何设置:当没有权限的时候,跳转到403路径的几种情况;
总结:因为开始没配置shiro的注解配置,所以图1-1 —> 图1-6只是基本的shiro配置,这里讲了会出现的几种情况.
但是后边用到了shiro注解的配置,却产生了权限不足时发生的不会自动跳转到403.html页面的异常,所以引入容器中了
解决授权无法捕获异常的异常,
以前通知是写在切面(AspectJ中),这里只写了一个通知
@RequiresGuest 注意授权的url必须在过滤器中被拦截
注意点: 这是个坑
1.当前用户如果被认证,不能访问
2.配置当前路径必须在ShiroFilterFactoryBean声明当前路径为 anon
shiro里边的一个坑,因为shiro要求,访问必须登录,但是
顾客访问不需要登录,所以要满足第二点.
图1-1的效果如何实现??? 通过图1-2,1-3,1-4,1-5来描述
图1-2 展示的是没有controller,有权限
图1-3 展示的是没权限,有controller
图1-4展示的是有权限,有controller
图1-5 展示的是添加了拦截器, 判断是否有role 和 权限, 这里三个异常情况(图1-7下有一个)
1.如果没有设置controller就会报错,
2.如果没有设置跳转的页面也会报错,这个异常需要抓取出来,并且当发生时,需要跳转到403页面.
图1-6 没有设置Realm的权限,或者设置了ShiroConfig拦截,Realm中却没有该权限.都不会弹500,会直接展示没有权限
图1-7 使用shiro注解时,如果用户Realm中没有设置该权限,那么访问的时候会报错.如何解决?通过异常的抓取去展示异常 图1-8可以解决该问题.
图1-8 解决授权无法捕获的问题
3.使用注解完成shiro的权限和鉴权简单操作: 需要引入AOP依赖
@Bean //基于注解的权限 鉴权 也就是说不通过上边复杂的方式进行 权限的限定
// @RequiresGuest,@RequiresRoles(),@RequiresUser,@RequiresPermissions()@RequiresAuthentication
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
// //将setSecurityManager与拦截器进行绑定
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourceAdvisor;
}
@Bean// <aop:aspectj-autoproxy/> 开启aop 注解,像容器注入一个 DefaultAdvisorAutoProxyCreator
//通知Advisor AOP.相当于<!-- 开启aop asjpectj 功能--> <aop:aspectj-autoproxy/>
@ConditionalOnMissingBean
DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =
new DefaultAdvisorAutoProxyCreator();
// 使用cglib 完成代理
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
com.aaa.springshiro.controller.GoodsController
@Controller
@RequestMapping("/goods")
public class GoodsController {
/*
*@Author lee
*@time:2020/7/29 21:15
*注释:在goods控制层展示的注解;
*/
@RequiresAuthentication// 只要当前 用户登陆就可以查看
@RequestMapping("/getGoodsList")
public String getGoodsList(){
return "goodslist";
}
@RequestMapping("/addGoods")
@RequiresPermissions("goods:add")// 要求访问当前路径 必须有goods:add 权限
public String addGoods(){
return "addgoods";
}
@RequestMapping("/deleteGoods")
@RequiresRoles("root") //要求访问当前 路径必须时boss
public String deleteGoods(){
return "deletegoods";
}
}
com.aaa.springshiro.controller.FirstController
package com.aaa.springshiro.controller;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/guest")
public class FirstController {
@RequestMapping("/first")
public String first(){
return "message";
}
@RequiresGuest
//1.要求当前用户是游客状态,必须是未登录 如果一定能路报错 注意当前/guest/first2
//2.必须filterChainDefinitionMap 设置当前路径为 anon 不拦截转台
@RequestMapping("/first2")
public String first2(){
return "message";
}
}
4. 抓取异常 抓取异常有三种方式,这里采用抓取前台异常的方式,去获取异常
/*这里的异常是从前台抓取的,你要记得你是后台,你要学会从后台抓取,如何抓? 这是个提升*/
@Bean //当前异常处理的就是 shiro 基于注解鉴权时, 不满足权限时需要抓住异常,因为不是所有的都会自动去403页面
//比如:GoodsController,FirstController中注解引起的错误,需要抓取异常;
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver =
new SimpleMappingExceptionResolver();
//Properties 就是读取配置文件的键対值
Properties property = new Properties();
//key 就是要捕捉异常的 全限定名
property.setProperty("org.apache.shiro.authz.AuthorizationException","/403.html");
resolver.setExceptionMappings(property);
return resolver;
}
本文地址:https://blog.csdn.net/nnnnnnnnnnii/article/details/107681184