springboot 整合shiro实现用户登录的认证、授权

前言

通过整合shiro来实现登录功能。

shiro有三个核心组件:Subject, SecurityManager 和 Realms.

  • Subject:代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。

  • SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
    (Facade模式:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节。)

  • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

注意:在下面的步骤中,我们会主要先实现认证,最后在认证的基础上增加资源授权功能

一、引入依赖

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

二、编写UserRealm类(仅认证)

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;//用于mybatis查询数据库用户信息的服务层

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        
        return null;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
        String username=token.getUsername();
        User userByName = userService.findUserByName(username);
        //如果用户根本不存在
        if(userByName==null)
        {
            return null;  //如果返回null,会抛出UnknownAccountException
        }
        //将真正的密码给到shiro 他会自动帮我们处理
        return  new SimpleAuthenticationInfo("",userByName.getPassword(),"");


    }
}

需要注意的是,在上面,如果用户不存在,我们直接返回null即可,shiro会自动抛出UnknownAccountException;如果用户存在但密码错误会抛出IncorrectCredentialsException,密码正确则不会抛出异常。

至于抛出这两个异常有什么用呢,我们后面再说。

三、创建shiroConfig配置类(仅认证)

@Configuration
public class shiroConfig {
    /**
     * 创建ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager)
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //添加shiro内置过滤器
        //常用的过滤器
        //anon  无需认证
        //authc 必须认证才可以访问
        //user  如果使用rememberme 的功能可以直接访问
        //perms 该资源必须授权资源权限才可以访问
        //role 必须得到角色权限才可以访问
        Map<String,String> filterMap = new LinkedHashMap<String,String>();

		//这里将add和update这种操作拦截
        filterMap.put("/html/addUserShiro.html","authc");
        filterMap.put("/html/updateUserShiro.html","authc");

        //设置拒绝后返回页面
        shiroFilterFactoryBean.setLoginUrl("/html/loginShiro.html");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;

    }
    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean(name ="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm)
    {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

        defaultWebSecurityManager.setRealm(userRealm);

        return defaultWebSecurityManager;
    }


    /**
     * 创建realm
     */
    @Bean(name ="userRealm") //使用Bean注解:方法返回的对象给spring管理
    public UserRealm getUserRealm()
    {
        return new UserRealm();
    }

}

四、编写Controller方便测试

   @RequestMapping(value="/loginShiro" )
    @ResponseBody
    public  RespBean loginShiro(HttpServletRequest httpServletRequest) throws IOException {
        String username=httpServletRequest.getParameter("username");
        String pwd=httpServletRequest.getParameter("password");
        //1. 获取subject
        Subject subject = SecurityUtils.getSubject();
        //2. 封装用户数据
        UsernamePasswordToken token=new UsernamePasswordToken(username,pwd);
        //3.执行登录判断
        try {
            subject.login(token);
            return RespBean.ok("success");
        }catch (UnknownAccountException e)
        {
            return RespBean.error("账户不存在!");
        }catch (IncorrectCredentialsException e)
        {
            return RespBean.error("密码错误!");
        }

    }

由于shiro会根据不同情况抛出UnknownAccountExceptionIncorrectCredentialsException异常,我们在上面的controller中进行捕获即可。

五、进行postman测试简单登录

我们对登录的url进行postman测试,数据库预先存储了用户名和密码都为wuyuehang的用户信息。

当登录账号密码都正确时
在这里插入图片描述
这个时候controller中不会抛出异常,所以登陆成功。

当登录用户不存在时,我们在前面直接返回了null,shiro将抛出UnknownAccountException异常,从而被controller捕获,返回账户不存在的信息。
在这里插入图片描述
当用户密码错误时,输入的密码被交给shiro,shiro发现密码错误,会抛出IncorrectCredentialsException异常,所以在controller中被捕获,从而返回密码错误的提示信息。
在这里插入图片描述
这是shiro与登录的结合。

六、实际进行认证测试

接下来,我们将测试这样一个功能:

  1. 若直接访问authc类型的界面,由于未被认证将自动跳转到登录界面。
  2. 在经过登录后,通过认证,便可以访问authc界面

我们在shiroConfig类中,对"/html/addUserShiro.html""/html/updateUserShiro.html" 两个路径拦截,并将这两个路径指定为必须经过认证后才可以正常访问。下面我们对其进行测试

  1. 首先直接访问"/html/addUserShiro.html",因为还没有认证,就会自动跳转到我们设定好的"/html/loginShiro.html"
    在这里插入图片描述

  2. 在被跳转的登陆界面上进行登录,登录成功后会跳转到我们一个测试界面
    在这里插入图片描述
    这两个的超链接分别对应前面我们设置authc,即需要认证的拦截界面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>查询成绩</title>
</head>
<body>
  <a href="/html/addUserShiro.html">add</a>
  <a href="/html/updateUserShiro.html">update</a>

</body>
</html>

随便点击一个后,我们可以发现经过登录认证后,我们进来了。
在这里插入图片描述

至此,用户经过认证(即登录成功)后,才可以访问被拦截的界面.

下一步,让我们来实现shiro的授权操作,授权的应用场景在于,某些用户尽管通过认证,但是我们依旧要对权限加以控制。

七、进行授权的设置

与认证一样,我们首要做的便是拦截器的设置,以资源授权perms为例,我们添加资源授权的拦截,

在shiroconfig类中的filterMap添加

 filterMap.put("/html/addUserShiro.html","perms[user_add]");

user_add字符串任意指定,但是这里字符串是什么后面,会需要。

这时,我们通过登录来完成认证之后,再点击/html/addUserShiro.html这个路径,便会出现如下界面,原先经过认证后能访问的资源界面就行不通了。
在这里插入图片描述
原因是:未授权,同样, 我们亦可以自定义一个未授权html界面,如下所示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>拒绝访问</title>
</head>
<body>
    <h1>您好,您没有访问该界面的权限!</h1>
</body>
</html>

在shiroconfig类中添加

 shiroFilterFactoryBean.setUnauthorizedUrl("/html/Unauthorized.html");

再次测试
在这里插入图片描述
我们接下来在UserRealm类的doGetAuthorizationInfo方法中编写授权的逻辑代码

 //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.addStringPermission("user_add");
        
        return info;
    }

这样,我们完成了资源的授权,跑一下测试
在这里插入图片描述
在经过认证后,本来不可以被访问,在该资源被授权后,访问html资源成功。

发布了200 篇原创文章 · 获赞 99 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_43889841/article/details/104010997