springboot整合shiro进行权限的验证

本文介绍如何在SpringBoot项目中整合Shiro框架进行权限验证,包括创建自定义Realm,配置ShiroFilter,以及实现登录和权限访问控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot整合shiro进行权限的验证

shiro

shiro是目前主流的java安全框架,主要用来更便捷的认证,授权,加密,会话管理。
验证的过程是:
1.创建SecurityManager安全管理器;
2.Subject主体带授权信息执行授权,请求到SecurityManager
3.SecurityManager安全管理器调用Authorizer授权
4.Authorizer结合主体一步步传过来的授权信息与Realm中的数据比对.
subject(用户)调用SecurityManager进行权限验证,然后SecurityManager根据Realm的规则仅限用户的身份和权限的比对.

Shiro架构

shiro的3个主要的组件:
Subject
SecurityManager
Realms
shiro
Subject:主体,当前参与应用安全部分的主体。可以是用户,可以是第三方服务,可以是cron 任务,或者任何东西。主要指一个正在与当前软件交互的东西。所有Subject都需要SecurityManager,当与Subject进行交互,这些交互行为实际上被转换为与SecurityManager的交互
SecurityManager:安全管理员,Shiro架构的核心,它就像Shiro内部所有原件的保护伞。然而一旦配置了SecurityManager,SecurityManager就用到的比较少,开发者大部分时间都花在Subject上面。当你与Subject进行交互的时候,实际上是SecurityManager在背后帮你举起Subject来做一些安全操作。
Realms:Realms作为Shiro和应用的连接桥,当需要与安全数据交互的时候,像用户账户,或者访问控制,Shiro就从一个或多个Realms中查找。Shiro提供了一些可以直接使用的Realms,如果默认的Realms不能满足你的需求,你也可以定制自己的Realms
Shiro整体架构

在这里插入图片描述
1.Authenticator:认证器,管理登陆与登出。

2.Authorizer:授权器,赋予主体权限。

3.Session Manager:session管理器,session管理机制。不借助任何web容器使用session

4.Session Dao:session操作,主要增删改查。

5.Cache Manager:缓存管理器

6.Pluggable Realms(1 or more):shiro与数据库的连接,认证授权校验

7.Cryptography:数据加密

Shiro核心组件

Subject 即主体,外部引用于 Subject 进行交互,Subject 记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject 在 Shrio 中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过 Sbject 进行认证授权,而 Sbject 是通过 SecurityManager 安全管理器进行认证授权的。

SecurityManager 即安全管理器,对全部的 Sbject 进行安全管理,它是 Shrio 的核心,通过 SecurityManager 可以完成全部 Sbject 的认证、授权等。实质上 SecurityManager 是通过 Authenticator 进行认证,通过 Authorizer 进行授权,通过 SessionManager 进行会话管理等。SecurityManager是一个接口,继承了 Authenticator ,Authorizer ,SessionManager 这三个接口。

Authenticator即认证器,对用户身份进行认证,Authenticator 是一个接口,Shrio 提供了 ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator 基本上可以满足大多数需求,也可以自定义认证器。
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

Realm即领域,相当于 datasource 数据源,SecurityManager 进行安全认证需要通过 Realm 获取用户权限数据,比如:如果用户身份数据在数据库,那么 Realm 就需要从数据库获取用户身份信息。不要把 Realm 理解成只是从数据源取数据,在 Realm 中还有认证授权校验的相关代码。

SessionManager 即会话管理,Shrio 框架定义了一套会话管理,它不依赖 web 容器的 Session ,所以Shrio 可以使用在非 Web 应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

SessionDAO 即会话DAO,是对 Session 会话操作的一套接口,比如要将 Session 存储到数据库,可以通过 jdbc 将会话存储到数据库。

CacheManager 即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

Cryptografy即密码管理,Shrio 提供了一套解密/加密的组件,方便开发。比如提供常用的散列、加/解密功能。

1.首先引入jar包

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

2.创建数据库表

在这里插入图片描述
姓名/密码和用户身份

3.自定realm验证规则

Realm中有3种验证的方式,InitRealm,JdbcRealm.和自定义的Realm.通常使用自定义的方式.
使用自定义的Realm继承AuthorizingRealm,并重写两个方法,一个是验证身份,一个是验证权限

public class MyRealm extends AuthorizingRealm {
    @Resource
    private RolesMapper rolesMapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("============用户权限==============");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        /*获取当前的用户*/
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        /*查询用户的权限*/
        String role = rolesMapper.getRole(username);
        /*将role放在一个集合中,多个权限使用集合set*/
        info.addRole(role);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("============用户验证==============");
        //从token中获取信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        String password = rolesMapper.getPassword(username);

        if (null == password) {
            throw new AuthenticationException("用户名不对");
        } else if (!password.equals(new String(token.getPassword()))){
            throw new AuthenticationException("密码不对");
        }
        //组合一个验证信息
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(token.getPrincipal(),password,getName());
        return info;
    }
}

配置shiroConfig配置类

@Configuration
public class ShiroConfig {
    /**
     * 创建自定义的验证规则
     * @return
     */
    @Bean
    public Realm myRealm() {
        return new MyRealm();
    }

    /**
     * 创建安全管理
     * 注意创建实现了web的对象
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        return manager;
    }

    /**
     * 创建shiro的过滤器,定义过滤规则
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        filter.setSecurityManager(securityManager());
        
        //拦截到没有登录后跳到哪里去登录
        filter.setLoginUrl("/notLogin");
       
        //拦截没有权限的用户跳到哪里去
        filter.setUnauthorizedUrl("/noAuthorize");
        
        /*进行权限的控制,必须使用LinkHashMap,shrio要按照顺序进行设置*/
        Map<String, String> authMap = new LinkedHashMap<>();
       
        //游客不拦截
        authMap.put("/guest/**", "anon");
        //只允许user权限的访问
        authMap.put("/user/**", "roles[user]");
        //只允许admin权限的访问
        authMap.put("/admin/**", "roles[admin]");
        //登录页面不可以拦截,
        authMap.put("/login", "anon");
        /*最后在写剩下的所有全部拦截,否则会造成拦截所有的url*/
        authMap.put("/**", "authc");
        
        filter.setFilterChainDefinitionMap(authMap);
        System.out.println("---------------shirofactory创建成功");
        return filter;
    }
注意点:

1, 一定要将自定义的MyRealm交给spring管理,否则异常找不到.
异常:java.lang.NullPointerException: null
at com.jd.shiro.MyRealm.doGetAuthenticationInfo(MyRealm.java:37) ~[classes/:na],
错误写法:manager.setRealm(new MyRealm());
2, 注意规则中的字母,authc和anon以及roles,否则报错
其实这些就是filter的key,shiro使用一个map维护filter链,每一个filter都有一个key,写错了就找不到对应的filter.
异常: There is no filter with name ‘anthc’ to apply to chain [/**] in the pool of available Filters. Ensure a filter with that name/path has first been registered with the addFilter method(s).
3, 注意将SecurityManager设置到ShiroFilterFactoryBean中,否则报错.
异常: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘shiroFilterFactoryBean’: FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanInitializationException: SecurityManager property must be set.
4,注意new的Security实现类一定要实现了WebSecurityManager接口,
注意是DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
而不是 DefaultSecurityManager manager = new DefaultSecurityManager();
异常:org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘shiroFilterFactoryBean’: FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanInitializationException: The security manager does not implement the WebSecurityManager interface
5.一定要使用import org.apache.shiro.mgt.SecurityManager;包下的SecurityManager,默认的是java的包,通不过编译

shiro的拦截规则

anon:表示可以匿名使用。

authc:表示需要认证(登录)才能使用,没有参数

roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,
当有多个参数时,例如admins/user/**=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。

perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,
例如/admins/user/**=perms[“user:add:,user:modify:”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

rest:根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

port:当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,
其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

authcBasic:没有参数表示httpBasic认证

ssl:表示安全的url请求,协议为https

user:当登入操作时不做检查

也可以自定一个filter加入到过滤链中.

测试准备

@Controller
public class LoginController {
    @Resource
    private RolesMapper rolesMapper;

    @RequestMapping(value = "/notLogin", method = RequestMethod.GET)
    public String notLogin() {
        return "login";
    }

    @RequestMapping("/main")
    public String test() {
        return "main";
    }


    @RequestMapping(value = "/noAuthorize")
    @ResponseBody
    public String notRole() {
        return "您没有权限!";
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    @ResponseBody
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        //注销
        subject.logout();
        return "成功注销!";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public String login(String name, String password) {
        //使用SecurityUtils创建一个当前的用户
        Subject subject = SecurityUtils.getSubject();
        
        //使用用户的关键信息创建一个token令牌用于验证
        UsernamePasswordToken token = new UsernamePasswordToken(name, password);
        // 执行认证登陆,这里会调用MyRealm中的doGetAuthenticationInfo方法进行身份验证
        subject.login(token);
        
        //根据权限,指定返回数据
        String role = rolesMapper.getRole(name);
        if ("admin".equals(role)) {
            return "欢迎来到管理员页面";
        } else if ("user".equals(role)) {
            return "欢迎用户使用";
        } else {
            return "欢迎游客访问";
        }
    }

只是验证登录的部分交给了shiro,其他的不变.

创建权限访问

user权限
@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping(value = "/msg", method = RequestMethod.GET)
    public String getMessage() {
        return "欢迎用户";
    }
}
admin权限
@RestController
@RequestMapping("/admin")
public class AdminController {
    @RequestMapping(value = "/msg", method = RequestMethod.GET)
    public String getMessage() {
        return "欢迎管理员";
    }
}

创建的套路都一样,代码就不全部放上了.

创建异常的拦截

@ControllerAdvice
public class GlobalException {

    @ExceptionHandler(Exception.class)
    public String globalException(Exception e) {
        return e.getMessage();
    }
}

前端的login和main

login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    姓名<input type="text" name="name"><br>
    密码<input type="password" name="password">
    <input type="submit" value="登录">
</form>
</body>
</html>
main.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>登录了</p>
</body>
</html>

测试

访问http://127.0.0.1:8080/main自动拦截跳转到notLogin然后转到login页面登录
在这里插入图片描述
在这里插入图片描述

输入账号密码返回相应的权限
在这里插入图片描述
当前是admin的权限,无法访问user权限的页面
访问user/msg会提示:
在这里插入图片描述

总结:
核心就是创建Realm和ShiroFilter.
其实说简单就是:
/*
* 自定义一个拦截的验证权限和验证身份的Realm对象,将值赋值SecurityManager进行安全的管理,
* 将SecurityManager对象赋值给ShiroFilterFactoryBean,让其进行安全的过滤.
* 注意设置一些拦截的顺序.
* */

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值