千家信息网

Springboot整合Shiro中怎样进行权限管理

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,这篇文章给大家介绍Springboot整合Shiro中怎样进行权限管理,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Shiro的授权流程跟认证流程类似:创建SecurityMan
千家信息网最后更新 2024年11月25日Springboot整合Shiro中怎样进行权限管理

这篇文章给大家介绍Springboot整合Shiro中怎样进行权限管理,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

Shiro的授权流程跟认证流程类似:

  1. 创建SecurityManager安全管理器

  2. Subject主体带授权信息执行授权,请求到SecurityManager

  3. SecurityManager安全管理器调用Authorizer授权

  4. Authorizer结合主体一步步传过来的授权信息与Realm中的数据比对,授权

(1)我们已经在ShiroConfig配置类中配置好了SecurityManager安全管理器

 /**     * 配置Shiro核心 安全管理器 SecurityManager     * SecurityManager安全管理器:所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;负责与后边介绍的其他组件进行交互。(类似于SpringMVC中的DispatcherServlet控制器)     */    @Bean    public SecurityManager securityManager(){        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();        //将自定义的realm交给SecurityManager管理        securityManager.setRealm(new CustomRealm());        return securityManager;    }

(2)下面对自定义Realm类CustomRealm中的授权方法doGetAuthorizationInfo进行重写

 @Override    /**     * 授权     */    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {        //获取当前登录的用户        User user = (User) principal.getPrimaryPrincipal();        //通过SimpleAuthenticationInfo做授权        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();        //添加角色        simpleAuthorizationInfo.addRole(user.getRole());        //添加权限        simpleAuthorizationInfo.addStringPermissions(user.getPermissions());        return simpleAuthorizationInfo;    }

上面主要通过SimpleAuthorizationInfo中的addRoleaddStringPermissions添加当前用户拥有的角色和权限,与主体的授权信息进行比对。
(3)主体调用授权请求
主体进行授权请求有两种方式,一种是编程式,一种是注解式。
①编程式:通过SubjecthasRole()进行角色的校检,通过isPermitted()进行权限的校检

@GetMapping("/dog")    public String dog(){        Subject subject = SecurityUtils.getSubject();        if(subject.hasRole("dog")){            return "dog√";        }       else {           return  "dog×";        }    }    @GetMapping("/cat")    public String cat(){        Subject subject = SecurityUtils.getSubject();        if(subject.hasRole("cat")){            return "cat√";        }        else {            return  "cat×";        }    } @GetMapping("/rap")    public String rap(){        Subject subject = SecurityUtils.getSubject();        if(subject.isPermitted("rap")){            return "rap";        }else{            return "没权限你Rap个锤子啊!";        }

模拟用户数据如下:

/**     * 模拟数据库数据     * @return     */    private List getUsers(){        List users = new ArrayList<>(2);        List cat = new ArrayList<>(3);        cat.add("sing");        cat.add("rap");        List dog = new ArrayList<>(3);        dog.add("jump");        dog.add("basketball");        users.add(new User("张小黑的猫","123qwe",true,"cat",cat));        users.add(new User("张小黑的狗","123qwe",true,"dog",dog));        return users;    }

测试结果如图:

authorization.gif

②注解式:
首先需要开启Aop注解,在ShiroConfig类中新增如下方法:

 /**     * 开启aop注解支持     * 即在controller中使用 @RequiresPermissions("user/userList")     */    @Bean    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){        AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();        //设置安全管理器        attributeSourceAdvisor.setSecurityManager(securityManager);        return attributeSourceAdvisor;    }    @Bean    @ConditionalOnMissingBean    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();        defaultAAP.setProxyTargetClass(true);        return defaultAAP;    }

对于未授权的用户需要进行友好的提示,一般返回特定的403页面,在ShiroConfig类中新增如下方法实现:

 /**     * 处理未授权的异常,返回自定义的错误页面(403)     * @return     */    @Bean    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();        Properties properties = new Properties();        /*未授权处理页*/        properties.setProperty("UnauthorizedException", "403.html");        resolver.setExceptionMappings(properties);        return resolver;    }

解释下上面的代码properties.setProperty("UnauthorizedException","403.html");用来对抓取到UnauthorizedException未授权异常进行处理,重定向到403.html页面。我们需要创建403页面403.html

        未授权403,您没有访问权限

然后在Controller使用注解@RequiresRoles("xxx")@RequiresPermissions("xxx")进行角色和权限的校检

    @GetMapping("/sing")    @RequiresRoles("cat")    public String sing(){        return "sing";    }    @GetMapping("/jump")    @RequiresPermissions("jump")    public String jump(){        return "jump";    }

测试结果如图:

authorization2.gif

总结:一般对未授权用户需要统一返回指定403页面的,使用注解更加方便;需要做业务逻辑(比如对未授权的请求记录进行预警等),使用编程式更加方便。
(4)前端的shiro标签
通常前端页面展示需要与用户的权限对等,即只给用户看到他们权限内的内容。(比如用户"张小黑的猫",对应角色"cat",对应权限"sing"和"rap",那么该用户登录后只展示Cat、sing和rap的按钮)
通常解决方式有两种:
其一:登录后通过读取数据库中角色和权限,获取需要展示的菜单内容,动态的在前端渲染;
其二:所有内容都在前端写好,通过前端的shiro标签控制对应权限内容部分的渲染。
这里给大家演示shiro标签的使用。(前端的shiro标签有Jsp标签、Freemarker标签、Thymeleaf标签等,演示用的为thymeleaf标签)

Shiro标签说明:guest标签:``,用户没有身份验证时显示相应信息,即游客访问信息。user标签:``,用户已经身份验证/记住我登录后显示相应的信息。authenticated标签:``,用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的。notAuthenticated标签:``,用户已经身份验证通过,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证。principal标签:``,相当`((User)Subject.getPrincipals()).getUsername()`。lacksPermission标签:``,如果当前Subject没有权限将显示body体内容。hasRole标签:``,如果当前Subject有角色将显示body体内容。hasAnyRoles标签:``,如果当前Subject有任意一个角色(或的关系)将显示body体内容。lacksRole标签:``,如果当前Subject没有角色将显示body体内容。hasPermission标签:``,如果当前Subject有权限将显示body体内容

使用:
①pom.xml引入相应的依赖

                     com.github.theborakompanioni            thymeleaf-extras-shiro            2.0.0        

注意:这里引用的版本是2.0.0,之前1.0.2有兼容问题
②ShiroConfig中加入配置

 /**     * 用于thymeleaf模板使用shiro标签     * @return     */    @Bean    public ShiroDialect shiroDialect() {        return new ShiroDialect();    }

③前端页面使用shiro标签

DogCat
SingJumpRapBasketball

注意:使用前现在html标签内引入shiro标签,即

CustomRealm.java:

package com.cdq.shriodemo.shiro;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import java.util.ArrayList;import java.util.List;/** * @Description: shiro的自定义realm * Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。 * @author ChenDeQuan * @data 2019-05-22 17:51 */public class CustomRealm extends AuthorizingRealm {    @Override    /**     * 认证     */    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        //1.获取用户输入的账号        String username = (String)token.getPrincipal();        //2.通过username模拟从数据库中查找到user实体        User user = getUserByUserName(username);        if(user == null){            return null;        }        //3.通过SimpleAuthenticationInfo做身份处理        SimpleAuthenticationInfo simpleAuthenticationInfo =                new SimpleAuthenticationInfo(user,user.getPassword(),getName());        //4.用户账号状态验证等其他业务操作        if(!user.getAvailable()){            throw new AuthenticationException("该账号已经被禁用");        }        //5.返回身份处理对象        return simpleAuthenticationInfo;    }    @Override    /**     * 授权     */    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {        //获取当前登录的用户        User user = (User) principal.getPrimaryPrincipal();        //通过SimpleAuthenticationInfo做授权        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();        //添加角色        simpleAuthorizationInfo.addRole(user.getRole());        //添加权限        simpleAuthorizationInfo.addStringPermissions(user.getPermissions());        return simpleAuthorizationInfo;    }    /**     * 模拟通过username从数据库中查找到user实体     * @param username     * @return     */    private User getUserByUserName(String username){        List users = getUsers();        for(User user : users){            if(user.getUsername().equals(username)){                return user;            }        }        return null;    }    /**     * 模拟数据库数据     * @return     */    private List getUsers(){        List users = new ArrayList<>(2);        List cat = new ArrayList<>(2);        cat.add("sing");        cat.add("rap");        List dog = new ArrayList<>(2);        dog.add("jump");        dog.add("basketball");        users.add(new User("cxk1","123",true,"cat",cat));        users.add(new User("cxk2","123",true,"dog",dog));        return users;    }}

ShiroConfig.java:

package com.cdq.shriodemo.shiro;import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.mgt.SecurityManager;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.LinkedHashMap;import java.util.Map;import java.util.Properties;/** * @Description springboot中的Shiro配置类 * @author ChenDeQuan * @data 2019-05-22 17:17 */@Configurationpublic class ShiroConfig {    /**     * 配置Shiro核心 安全管理器 SecurityManager     * SecurityManager安全管理器:所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;负责与后边介绍的其他组件进行交互。(类似于SpringMVC中的DispatcherServlet控制器)     */    @Bean    public SecurityManager securityManager(){        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();        //将自定义的realm交给SecurityManager管理        securityManager.setRealm(new CustomRealm());        return securityManager;    }    /**     * 配置Shiro的Web过滤器,拦截浏览器请求并交给SecurityManager处理     * @return     */    @Bean    public ShiroFilterFactoryBean webFilter(SecurityManager securityManager){        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();        //设置securityManager        shiroFilterFactoryBean.setSecurityManager(securityManager);        //配置拦截链 使用LinkedHashMap,因为LinkedHashMap是有序的,shiro会根据添加的顺序进行拦截        // Map K指的是拦截的url V值的是该url是否拦截        Map filterChainMap = new LinkedHashMap(16);        //配置退出过滤器logout,由shiro实现        filterChainMap.put("/logout","logout");        //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问,先配置anon再配置authc。        filterChainMap.put("/login","anon");        filterChainMap.put("/**", "authc");        //设置默认登录的URL.        shiroFilterFactoryBean.setLoginUrl("/login");        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);        return shiroFilterFactoryBean;    }    /**     * 开启aop注解支持     * 即在controller中使用 @RequiresPermissions("user/userList")     */    @Bean    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){        AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();        //设置安全管理器        attributeSourceAdvisor.setSecurityManager(securityManager);        return attributeSourceAdvisor;    }    @Bean    @ConditionalOnMissingBean    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();        defaultAAP.setProxyTargetClass(true);        return defaultAAP;    }    /**     * 处理未授权的异常,返回自定义的错误页面(403)     * @return     */    @Bean    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();        Properties properties = new Properties();        /*未授权处理页*/        properties.setProperty("UnauthorizedException", "403.html");        resolver.setExceptionMappings(properties);        return resolver;    }    /**     * 用于thymeleaf模板使用shiro标签     * @return     */    @Bean    public ShiroDialect shiroDialect() {        return new ShiroDialect();    }}

User.java:

package com.cdq.shriodemo.shiro;import java.util.List;/** * @Description 用户 * @author ChenDeQuan * @data 2019-05-22 19:18 */public class User {    private String username;    private String password;    private Boolean available;    private String role;    private List permissions;    public User(String username, String password, Boolean available, String role, List permissions) {        this.username = username;        this.password = password;        this.available = available;        this.role = role;        this.permissions = permissions;    }    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public Boolean getAvailable() {        return available;    }    public void setAvailable(Boolean available) {        this.available = available;    }    public String getRole() {        return role;    }    public void setRole(String role) {        this.role = role;    }    public List getPermissions() {        return permissions;    }    public void setPermissions(List permissions) {        this.permissions = permissions;    }}

success.html:

        success
DogCat
SingJumpRapBasketball

关于Springboot整合Shiro中怎样进行权限管理就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0