ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Shiro集成springboot的一些配置和问题

2021-12-07 15:59:31  阅读:101  来源: 互联网

标签:集成 return springboot 登录 user shiro new public Shiro


首先贴上官网地址https://shiro.apache.org/spring-framework.html

一、shiro配置类

要使用shiro必须配置一个shiroConfig配置类,中shiro配置的类中需要三个bean:

  • Realm,自定义的Realm
  • DeaultWebSecurityManager,需要传递Realm参数
  • ShiroFilterFactoryBean,需要传递DeaultWebSecurityManager参数
    • 在ShiroFilterFactoryBean里面我们可以选择添加对应的过滤器
      • anon : 无需认证,就可以访问
      • authc : 必须认证,才能访问
      • user : 必须拥有 “记住我”功能才能用
      • perms : 拥有对某个资源的权限才能访问
      • role : 拥有某个角色权限才能访问
@Configuration
public class ShiroConfig {

    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

    @Bean
    public DefaultWebSecurityManager defaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultSecurityManager") DefaultWebSecurityManager SecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(SecurityManager);

        //添加 Shiro 的内置过滤器=======================
        /*
            anon : 无需认证,就可以访问
            authc : 必须认证,才能访问
            user : 必须拥有 “记住我”功能才能用
            perms : 拥有对某个资源的权限才能访问
            role : 拥有某个角色权限才能访问
         */
        Map<String, String> map = new LinkedHashMap<>();
        map.put("/back/**","authc");
        map.put("/front/**","anon");
        // 设置 /user/addUser 这个请求,只有认证过才能访问
        //        map.put("/user/addUser","authc");
        //        map.put("/user/deleteUser","authc");
        // 设置 /user/ 下面的所有请求,只有认证过才能访问
        //        map.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(map);
        // 设置登录的请求
        bean.setLoginUrl("/login");
        //============================================
        bean.setUnauthorizedUrl("/noPermission");
        return bean;
    }

    //thymeleaf集成shiro
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }


    @Bean
    public ShiroExceptionResolver shiroExceptionResolver(){
        return new ShiroExceptionResolver();
    }

}

shiro内置的几种过滤器

anon : 无需认证,就可以访问
authc : 必须认证,才能访问
user : 必须拥有 “记住我”功能才能用
perms : 拥有对某个资源的权限才能访问
role : 拥有某个角色权限才能访问

二、shiro-realm认证和授权

自定义的Realm需要集成AuthorizingRealm,重写以下方法

  • doGetAuthorizationInfo(PrincipalCollection principalCollection)Authorization授权
    • 首先doGetAuthorizationInfo需要return一个AuthorizationInfo对象,我们先new一个他的实现类对象:SimpleAuthorizationInfo
      • image-20211207144530901
    • 授权需要我们拿到登录对象—>登录用户:SecurityUtils.getSubject(); ,***(User)subject.getPrincipal();***
    • SimpleAuthorizationInfo里面有授权,授予角色的函数
      • addRole(String role)
      • setRoles(Set<String> set)
      • addStringPermissions(String permission)
      • addObjectPermissions(Set<Permission>)
    • 最后对用户授权完毕后返回info
  • doGetAuthenticationInfo(AuthenticationToken authenticationToken)Authentication认证
    • 首先doGetAuthenticationInfo需要return一个AuthenticationInfo对象,我们先new一个他的实现类对象:SimpleAuthenticationInfo
      • image-20211207145349884
      • 接着看这个实现类的构造函数image-20211207145507639
        • 常用的前两个,传递给该构造函数一个用户principal,credentials登录凭证,一个realName当前登录的角色名,这个在下一层会封装进SimplePrincipalCollection集合里面
    • 所以此时我们需要获取数据库的用户信息,将其交给SimpleAuthenticationInfo来进行验证
    • 登录验证时,我们在Controller里对接收的用户名密码封装为shiro里面的UsernamePasswordToken令牌(此时并未验证),之后Realm就能在后面doGetAuthenticationInfon拿到登录的令牌
    • doGetAuthenticationInfon拿到登录的令牌能获取用户名,我们就通过查询用户名获取密码进行验证
      • 用户名不存在抛出UnknownAccountException异常
      • 密码错误抛出IncorrectCredentialsException异常
    • 我们就可以捕获异常进行相应的处理

LoginController

@PostMapping("/LoginConfirm")
    public String login(String username, String password, Model model) {
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //没有认证过
        //封装用户的登录数据,获得令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //登录 及 异常处理
        try {
            //用户登录
            subject.login(token);
            Session subjectSession = subject.getSession();
            model.addAttribute("loginUser",subjectSession.getAttribute("loginUser"));
            return "redirect:index";
        } catch (UnknownAccountException uae) {
            log.warn("用户名不存在");
            //如果用户名不存在
            System.out.println("用户名不存在");
            model.addAttribute("exception", "用户名不存在");
            return "redirect:login";
        } catch (IncorrectCredentialsException ice) {
            log.warn("密码错误");
            //如果密码错误
            System.out.println("密码错误");
            model.addAttribute("exception", "密码错误");
            return "redirect:login";
        }
}

UserRealm

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("================执行了授权====================");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 拿到当前登录的这个对象
        Subject subject = SecurityUtils.getSubject();
        // 拿到User对象
        User currentUser = (User) subject.getPrincipal();
        // 设置当前用户的权限
        String permission = currentUser.getPermission();
        System.out.println(currentUser.getUsername() + "的权限为 " + permission);
        info.addStringPermission(permission);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("=====================执行了认证AuthenticationToken===================");

        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
        //连接真实的数据库
        User user = userService.getUserByName(userToken.getUsername());
        if(user == null){
            //没有这个人
            return null; //抛出异常 UnknownAccountException
        }

        // 登录成功 将用户信息存入session
        Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser",user);

        // 密码认证,shiro做
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

三、shiro-thymeleaf

配置:

要想在前端使用shiro的标签要进行如下配置

在刚才的shiro配置类里新增如下Bean

@Configuration
public class ShiroConfig {

    //thymeleaf集成shiro
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

}

在thymeleaf前端<html>标签里加入:

xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

image-20211207153210785

常用标签

<shiro:guest>
    游客访问 <a href = "login.jsp"></a>
</shiro:guest>
 
user 标签:用户已经通过认证\记住我 登录后显示响应的内容
<shiro:user>
    欢迎[<shiro:principal/>]登录 <a href = "logout">退出</a>
</shiro:user>
 
authenticated标签:用户身份验证通过,即 Subjec.login 登录成功 不是记住我登录的
<shiro:authenticted>
    用户[<shiro:principal/>] 已身份验证通过
</shiro:authenticted>
 
notAuthenticated标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括"记住我"也属于未进行身份验证
<shiro:notAuthenticated>
    未身份验证(包括"记住我")
</shiro:notAuthenticated>
 
 
principal 标签:显示用户身份信息,默认调用
Subjec.getPrincipal()获取,即Primary Principal
<shiro:principal property = "username"/>
 
hasRole标签:如果当前Subject有角色将显示body体内的内容
<shiro:hashRole name = "admin">
    用户[<shiro:principal/>]拥有角色admin
</shiro:hashRole>
 
hasAnyRoles标签:如果Subject有任意一个角色(或的关系)将显示body体里的内容
<shiro:hasAnyRoles name = "admin,user">
    用户[<shiro:pricipal/>]拥有角色admin 或者 user
</shiro:hasAnyRoles>
 
lacksRole:如果当前 Subjec没有角色将显示body体内的内容
<shiro:lacksRole name = "admin">
    用户[<shiro:pricipal/>]没有角色admin
</shiro:lacksRole>
 
hashPermission:如果当前Subject有权限将显示body体内容
<shiro:hashPermission name = "user:create">
    用户[<shiro:pricipal/>] 拥有权限user:create
</shiro:hashPermission>
 
lacksPermission:如果当前Subject没有权限将显示body体内容
<shiro:lacksPermission name = "org:create">
    用户[<shiro:pricipal/>] 没有权限org:create
</shiro:lacksPermission>	

四、开发过程中遇到的问题

1、ShiroFilterFactoryBean配置了setUnauthorizedUrl不起作用

启发来源:https://blog.csdn.net/bicheng4769/article/details/86680955

问题重现:

在ShiroFilterFactoryBean中配置setUnauthorizedUrl,顾名思义未授权则跳转至对应请求路径

image-20211207153333925

但是却发现无论怎么样,无权限的只会报错而不会跳到配置的那个请求

原因追溯:

我们先点进setUnauthorizedUrl看源码,发现这个url在applyUnauthorizedUrlIfNecessary里面使用

image-20211207154038379

我们发现AuthorizationFilter这个过滤器使用了该url,点进去看实现类

image-20211207154127514

发现AuthorizationFilter下的实现类只有Port,Premission,Role,Ssl这些过滤器;

而我在前面配置的FilterChainDefinitionMap里面是authc和anon类型的,不在该过滤器管理范围内,那么就会一直抛出异常org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method

解决:

手动捕获异常进行页面跳转

ShiroExceptionResolver实现HandlerExceptionResolver,当遇到该异常时手动跳转至位授权的页面

public class ShiroExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("==============异常开始=============");
        //如果是shiro无权操作,因为shiro 在操作auno等一部分不进行转发至无权限url
        if(ex instanceof UnauthorizedException){
            ModelAndView mv = new ModelAndView("redirect:/noPermission");
            return mv;
        }
        ex.printStackTrace();
        System.out.println("==============异常结束=============");
        ModelAndView mv = new ModelAndView("redirect:/err");
        mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>"));
        return mv;
    }
}

总结:

perms,roles,ssl,rest,port 属于AuthorizationFilter

anon,authcBasic,auchc,user 属于 AuthenticationFilter

标签:集成,return,springboot,登录,user,shiro,new,public,Shiro
来源: https://blog.csdn.net/qq_45380083/article/details/121768311

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有