springboot字段权限控制 springboot 接口权限控制

admin2024-06-01  11

本文搭建环境使用Springboot-2.1.3版本,数据库使用mysql,持久层使用jpa

简单实现了用户权限登录的功能,实现的比较简洁,实际项目中根据需求进行修改

1. Pom文件

首先导入需要的包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>Shiro</groupId>
    <artifactId>Shiro</artifactId>
    <version>Shiro-1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.20</version>
        </dependency>

        <!-- 安全框架apache shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>

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

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</project>

直接导入就可以了。

2.配置文件application.yml

server:
  port: 8081

#database
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    username: root
    password: 123456
    initialization-mode: always
    schema: classpath:schema.sql

3.数据库配置

数据源的配置

package com.shiro.build.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * @author FCZ
 * @since 2019/3/1 15:22
 * 数据源配置
 */
@Configuration
public class DatabaseConfiguration {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean(name = "dataSource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

}

druid连接池配置

package com.shiro.build.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Druid数据源配置
 */
@Configuration
public class DruidConfig {

    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean reg = new ServletRegistrationBean();
        reg.setServlet(new StatViewServlet());
        reg.addUrlMappings("/druid/*");
        reg.addInitParameter("loginUsername", "admin");
        reg.addInitParameter("loginPassword", "admin");
        return reg;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

4.获取用户数据

springboot字段权限控制 springboot 接口权限控制,springboot字段权限控制 springboot 接口权限控制_Springboot,第1张

使用jpa查询用户权限 以及用户信息,详情请查看GitHub中的代码.

5.Shiro的实现

最重要的shiro的配置实现,主要使用两个类进行配置 

MyShiroRealm继承AuthorizingRealm,实现AuthorizingRealm的doGetAuthorizationInfo()和doGetAuthenticationInfo()方法.用于进行登录时的认证和授权。
package com.shiro.build.shiro;

import com.shiro.build.entity.RoleInfo;
import com.shiro.build.entity.UserInfo;
import com.shiro.build.respository.RoleRepository;
import com.shiro.build.respository.UserRepository;
import org.apache.shiro.authc.*;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * @author FCZ
 * @since 2019/3/1 16:22
 */
public class MyShiroRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RoleRepository roleRepository;

    /**
     * 权限认证 给登陆的用户授予角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("执行Shiro权限认证");
        //获取当前登录输入的用户名
        String loginName = (String) super.getAvailablePrincipal(principalCollection);

        // 到数据库查是否有此对象
        UserInfo loginUserInfo = userRepository.findUserInfoByUserName(loginName);
        if (loginUserInfo != null) {
            // 取得权限信息
            List<RoleInfo> roleInfoList = roleRepository.getRoleInfoByUserID(loginUserInfo.getId());
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            Set<String> userRoles = new HashSet<>();
            for(RoleInfo roleInfo : roleInfoList) {
                userRoles.add(roleInfo.getRoleName());
            }
//            info.addStringPermissions(userPermissions);
            info.addRoles(userRoles);
            return info;
        }
        // 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址
        return null;
    }

    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //UsernamePasswordToken 对象用来存放提交的登录信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //查出是否有此用户
        UserInfo loginUserInfo = userRepository.findUserInfoByUserName(token.getUsername());

        if (loginUserInfo != null) {
            // 若存在,将此用户存放到登录认证info中
            // 第一个参数为成功后存储到session中的信息 获取通过   SecurityUtils.getSubject().getPrincipal()进行获取
            return new SimpleAuthenticationInfo(loginUserInfo, loginUserInfo.getPassword(), getName());
        }
        return null;
    }

}
ShiroConfiguration类,对shiro进行配置,分别进行了对realm的注册、用户密码的加密规则以及对URL的过滤规则、和权限控制
package com.shiro.build.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Created by hp on 2017/7/26.
 */
@Configuration
public class ShiroConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);

    /**
     * 安全管理器
     *
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getMyRealm());
        return securityManager;
    }

    /**
     * Realm
     *
     * @return
     */
    @Bean(name = "myRealm")
    public MyShiroRealm getMyRealm() {
        MyShiroRealm realm = new MyShiroRealm();
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }

    /**
     * 加密规则
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //md5加密 散列1次
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(1);
        return hashedCredentialsMatcher;
    }

/*    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }*/

    /**
     * 开启Shiro的注解支持
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    /**
     * 开启aop注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager());
        return aasa;
    }

    /**
     * 过滤规则
     *
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorizedRole");

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // authc 必须验证后才能访问
        // anon 不需要验证
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/api/**", "authc");

        // 除了使用注解@RequiresRoles 配置权限 还可以通过下面进行配置
        filterChainDefinitionMap.put("/user/**", "roles[USER]");
        filterChainDefinitionMap.put("/admin/**", "roles[ADMIN]");

        // "/**" 拦截 必须放到最下面
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    // 异常配置
    // 使用@RequiresRoles 注解,不跳转
    @Bean
    public SimpleMappingExceptionResolver resolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/unauthorizedRole");
        resolver.setExceptionMappings(properties);
        return resolver;
    }
}

基本这样,就已经完成了项目的搭建工作。

然后启动项目调用对应接口查看权限。

登录用户 该用户有USER权限,没有ADMIN权限。

 

springboot字段权限控制 springboot 接口权限控制,springboot字段权限控制 springboot 接口权限控制_springboot字段权限控制_02,第2张

调用USER权限接口,查看是否调用成功

springboot字段权限控制 springboot 接口权限控制,springboot字段权限控制 springboot 接口权限控制_spring_03,第3张

调用ADMIN权限接口,查看是否调用成功

springboot字段权限控制 springboot 接口权限控制,springboot字段权限控制 springboot 接口权限控制_springboot字段权限控制_04,第4张

结果显示与配置的是一致的。

下面是我搭建环境时候遇见的一些问题的总结

1. 配置未授权url后不生效

shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorizedRole");

使用这种方式配置后,访问未授权的接口,并没有跳转到"/unauthorizedRole"接口,只会提示404。

filterChainDefinitionMap.put("/user/**", "roles[USER]");
filterChainDefinitionMap.put("/admin/**", "roles[ADMIN]");

使用这样的方式进行配置,就可以跳转到"/unauthorizedRole"接口。

或者在配置文件中,增加对异常的处理

@Bean
    public SimpleMappingExceptionResolver resolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/unauthorizedRole");
        resolver.setExceptionMappings(properties);
        return resolver;
    }

这样就可以解决上述问题。

2.第一次调用登录接口会提示404错误。

因为第一次访问没有存在的cookie信息,会导致这个问题。再次请求即可。正式发布使用不会出现这种问题。

Github地址 GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!