189 8069 5689

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标签


Dog
Cat

Sing Jump Rap Basketball

注意:使用前现在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
 */
@Configuration
public 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




Dog Cat
Sing Jump Rap Basketball

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


网页名称:Springboot整合Shiro中怎样进行权限管理
文章出自:http://cdxtjz.com/article/jhdood.html