Spring Security (thirteen) [authorization]

13. Authorization


  • what is authorization
  • Core Concepts of Rights Management
  • Spring Security permission management strategy
  • Rights management based on URL address
  • Method-Based Rights Management
  • combat

authority management

Identity authentication is the process of judging whether a user is a legitimate user. Spring Security supports a variety of different authentication methods, but no matter which authentication method the developer uses, it will not affect the use of the authorization function. Because Spring Security does a good job of decoupling authentication and authorization

authorized

Authorization , or access control, controls who can access which resources. A simple understanding of authorization is to assign users permission to access a certain resource according to the rules set in advance by the system, and users perform corresponding operations according to their own permissions

Authorization Core Concepts

In the previous learning authentication process, we learned that after the authentication is successful, the current login user information will be saved in the Authentication object. There is a getAuthorities () method in the Authentication object , which is used to return the authority information of the current login user, that is, the current user Has permission information. The return value of this method is Collection<? extends GrantedAuthority>. When authority judgment is required, the corresponding method will be called to judge according to the authority information returned by the collection.

insert image description here

So the question is, how should we understand the return value GrantedAuthority ? Is it a role or a permission?
We can refer to authorization 基于角色权限管理and 基于资源权限管理, from the design level, roles and permissions are two completely different things: permissions are some specific operations, and roles are a collection of certain permissions. Such as:
READ_BOOK and ROLE_ADMIN are completely different. So what the return value is depends on your business design:

  • The design based on role permissions is: 用户<=>角色<=>资源the return of the relationship between the three is the user's角色
  • The design based on resource permissions is: 用户<=>权限<=>资源the return of the relationship between the three is the user's权限
  • The design based on role and resource permissions is: 用户<=>角色<=>权限<=>资源return the权限

Why can it be collectively referred to as permissions, because roles and permissions are not much different from the code level, they are all permissions, especially in Spring Security, roles and permissions are basically handled in the same way. The only difference is that Spring Security will automatically add a ROLE_prefix to roles in many cases, but permissions will not automatically add

13.1 Rights Management Policy

There are two main types of authority management strategies provided in Spring Security

  • Filter-based rights management (FilterSecurityInterceptor)
    • The authority management based on the filter (URL) is mainly used to intercept the HTTP request, and after interception, the authority verification is performed according to the HTTP request address
  • AOP-based rights management (MethodSecruityInterceptor)
    • Based on AOP (method) rights management is mainly used to deal with method-level rights issues. When a method needs to be called, the operation is intercepted through AOP, and then it is judged whether the user has the relevant authority

URL-based authority management

  • controller
package com.vinjcent.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * 自定义 spring security 配置类
 */
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    

    @Bean
    public UserDetailsService userDetailsService() {
    
    

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("ADMIN").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("lisi").password("{noop}123").roles("USER").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("win7").password("{noop}123").authorities("READ_INFO").build());

        return inMemoryUserDetailsManager;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests()
                .mvcMatchers("/admin/**").hasRole("ADMIN")  // 访问 admin 角色
                .mvcMatchers("/user/**").hasAnyRole("USER", "ADMIN")    // 访问 user 角色
                .mvcMatchers("/info/**").hasAuthority("READ_INFO")   // 访问 READ_INFO 权限
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable();
    }
}
  • permission expression

insert image description here

method illustrate
hasAuthority(String authority) Whether the current user has the specified permissions
hasAnyAuthority(String… authorities) Whether the current user has any of the specified permissions
hasRole(String role) Whether the current user has the specified role
hasAnyRole(String… roles) Whether the current user has any of the specified roles
permitAll() Release all requests/calls
denyAll() Deny all requests/calls
isAnonymous() Whether the current user is an anonymous user
isAuthenticated() Whether the current user has successfully authenticated
isRememberMe() Whether the current user is automatically logged in via RememberMe
isFullyAuthenticated() Whether the current user is neither anonymous nor automatically logged in via RememberMe
hasPermission(Object target, Object permission) Whether the current user has the specified permission information of the specified target
hasPermission(Object targetId, String targetType, Object permission) Whether the current user has the specified permission information of the specified target

Method-Based Rights Management

Method-based rights management is mainly implemented through AOP, and Spring Security provides related implementations through MethodSecurityInterceptor . The difference is that FilterSecurityInterceptor only performs pre-processing before the request, and MethodSecurityInterceptor can also perform post-processing in addition to pre-processing. Pre-processing is to determine whether you have the corresponding permissions before the request, and post-processing is to perform secondary filtering on the execution results of the method. Pre-processing and post-processing correspond to different implementation classes

@EnableGlobalMethodSecurity

@EnableGlobalMethodSecurity This annotation is used to enable the permission annotation, the usage is as follows

/**
 * 自定义 spring security 配置类
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    }
  • perPostEnabled : Open the four permission annotations provided by Spring Security, @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter
  • securedEnabled : Enable the @Secured annotation support provided by Spring Security, which does not support permission expressions
  • jsr250Enabled : Enable the annotations provided by JSR-250, mainly @DenyAll, @PermitAll, @RolesAllowed These annotations also do not support permission expressions

The meaning of the above annotations is as follows

  • @PreAuthorize: Permission verification is performed before the target method is executed

  • @PreFiter: Filter the method parameters before the target method is executed

  • @PostAuthorize: Permission verification is performed after the target method is executed

  • @PostFiter: Filter the return result of the method after the target method is executed

  • @Secured: The access target method must have the corresponding role

  • @DenyAll: deny all access

  • @PermitAll: allow all access

  • @RolesAllowed: The access target method must have the corresponding role

    These annotations related to method-based rights management are generally enough to be prePostEnable=trueset

  • use case test (Need to add in custom Spring Security configuration class@EnableGlobalMethodSecurityannotation

package com.vinjcent.controller;

import com.vinjcent.pojo.User;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.web.bind.annotation.*;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import java.util.ArrayList;
import java.util.List;

@RestController
public class DemoController {
    
    

    /**
     * 方法执行前
     * 角色必须是 "ADMIN" 并且用户名是 "root" 或有 "READ_INFO" 权限
     * @return 字符串
     */
    @PreAuthorize("hasRole('ADMIN') and authentication.name == 'root' or hasAuthority('READ_INFO')")
    @GetMapping("/hello")
    public String hello() {
    
    
        return "admin ok!";
    }

    /**
     * 方法执行前
     * 当前认证的用户名必须与传递的参数username一致
     * @param username  用户名
     * @return  字符串
     */
    @PreAuthorize("authentication.name == #username")
    @GetMapping("/name")
    public String hello(String username) {
    
    
        return "hello: " + username;
    }

    /**
     * 方法执行前
     * 当前的过滤对象数组中,id 为 奇数的用户
     * @param users 过滤后的用户
     */
    @PreFilter(value = "filterObject.id % 2 != 0", filterTarget = "users")
    @PostMapping("/users")
    public void addUsers(@RequestBody List<User> users) {
    
       // filterTarget 必须是 数组、集合类型
        System.out.println("users = " + users);
    }

    /**
     * 方法执行后
     * 返回的对象,id只能为"1"
     * @param id    传递的id参数
     * @return  返回一个对象
     */
    @PostAuthorize("returnObject.id == 1")
    @GetMapping("/userId")
    public User getUserById(Integer id) {
    
    
        return new User(id, "vinjcent");
    }

    /**
     * 方法执行完后
     * 将集合中用户id为偶数的用户进行过滤
     * @return  用户集合
     */
    @PostFilter("filterObject.id % 2 == 0")
    @GetMapping("/users")
    public List<User> getAllUsers() {
    
    
        ArrayList<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            users.add(new User(i, "user" + i));
        }
        return users;
    }

    /**
     * 当前认证的用户必须有指定角色,才能访问该方法
     * @return  返回一个对象
     */
    @Secured({
    
    "ROLE_USER", "ROLE_ADMIN"})   // 可以写多个角色,具有其中一个即可
    @GetMapping("/secured")
    public User getUserByUsername() {
    
    
        return new User(99, "secured");
    }

    /**
     * 允许所有访问
     * @return  字符串
     */
    @PermitAll
    @GetMapping("/permitAll")
    public String permitAll() {
    
    
        return "permitAll";
    }

    /**
     * 拒绝所有访问
     * @return  字符串
     */
    @DenyAll
    @GetMapping("/denyAll")
    public String denyAll() {
    
    
        return "denyAll";
    }

    @RolesAllowed({
    
    "ROLE_USER", "ROLE_ADMIN"})   // 可以写多个角色,具有其中一个即可
    @GetMapping("/rolesAllowed")
    public String roleAllowed() {
    
    
        return "rolesAllowed";
    }
}

13.2 antMatchers()、mvcMatchers()、regexMatchers()

  • antMatchers : Map AntPathRequestMatcher instance List

  • mvcMatchers : This matcher will use the same rules that Spring MVC uses for matching. For example, a mapping for the path "/path" will usually match "/path", "/path/", "/path.html"

  • regexMatchers : regular expression matchers

insert image description here

insert image description here

13.3 Principle Analysis of Authorization

Authorize Debug

  • First, it will go through the doFilter () method in the FilterSecurityInterceptor class

insert image description here

  • Then it will go through the super.beforeInvocation () method of the parent class

insert image description here

  • Then go to this.obtainSecurityMetadataSource().getAttributes(object) of the parent class to get the corresponding metadata (Corresponding permissions of custom resources

insert image description here

  • Then similar to authentication, try to authorize attemptAuthorization()

insert image description here

  • During authorization, the decide () method in the AccessDecisionManager interface will be used to vote

insert image description here

  • Loop through the permissions of the authenticated user

insert image description here

  • Brief process

insert image description here

  • ConfigAttribute In Spring Security, the role required by the user to request a resource (usually an interface or a Java method) will be encapsulated into a ConfigAttribute object. There is only one getAttribute method in ConfigAttribute, which returns a String string, which is the role name. Generally speaking, the role name has a ROLE_prefix. What the voter AccessDecisionVoter does is to compare the relationship between the user's role and the ConfigAttribute required to request a certain resource.
  • Both AccessDecisionVoter and AccessDecisionManager have many implementation classes. AccessDecisionVoter will be traversed one by one in AccessDecisionManager to decide whether to allow users to access. Therefore, the relationship between AccessDecisionVoter and AccessDecisionManager is similar to the relationship between AuthenticationProvider and ProviderManager

13.4 Custom Resource Permissions in Practice

In the previous case, we configure the URL interception rules and the permissions required to request the URL are configured through code, which is relatively fixed. If you want to adjust the permissions required to access a certain URL, you need to modify the code

The dynamic management permission rule means that we store the URL interception rules and the permissions required to access the URL in the database, so that without modifying the source code, we only need to modify the data in the database to adjust the permissions

用户<--中间表-->角色<--中间表-->菜单

Library table design

-- menu 表
CREATE TABLE `menu` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '菜单id',
  `pattern` VARCHAR(128) DEFAULT NULL COMMENT '路径映射',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8


-- user 表
CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `username` VARCHAR(32) DEFAULT NULL COMMENT '用户名',
  `password` VARCHAR(255) DEFAULT NULL COMMENT '密码',
  `enabled` TINYINT(1) DEFAULT NULL COMMENT '是否可用',
  `locked` TINYINT(1) DEFAULT NULL COMMENT '是否锁定',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

-- role 表
CREATE TABLE `role` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `name` VARCHAR(32) DEFAULT NULL COMMENT '角色英文名称',
  `nameZh` VARCHAR(32) DEFAULT NULL COMMENT '角色中文名称',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

-- menu_role 表
CREATE TABLE `menu_role` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '菜单角色id',
  `mid` INT(11) DEFAULT NULL COMMENT '菜单id',
  `rid` INT(11) DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`),
  KEY `mid` (`mid`),
  KEY `rid` (`rid`),
  CONSTRAINT `menu_role_ibfk_1` FOREIGN KEY (`mid`) REFERENCES `menu` (`id`),
  CONSTRAINT `menu_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

-- user_role 表
CREATE TABLE `user_role` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户角色id',
  `uid` INT(11) DEFAULT NULL COMMENT '用户id',
  `rid` INT(11) DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`),
  KEY `rid` (`rid`),
  CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`),
  CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
  • reference data

insert image description here

Dependencies and configuration files

  • pom.xml
<dependencies>
    <!--security-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
</dependencies>
  • application.yml
# 端口号
server:
  port: 8080

spring:
  application:
    # 应用名称
    name: SpringSecurity014
  # 数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springsecurity?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
# mybatis
mybatis:
  type-aliases-package: com.vinjcent.pojo
  mapper-locations: classpath:com/vinjcent/mapper/*.xml

# 日志打印
logging:
  level:
    com:
      vinjcent:
        debug

code design

  • Corresponding entity class

insert image description here

  • Mapper interface

1)UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vinjcent.mapper.UserMapper">


    <!--根据用户名查询用户信息-->
    <select id="queryUserByUsername" resultType="User">
        select id,
               username,
               password,
               enabled,
               locked
        from user
        where username = #{username}
    </select>

</mapper>

2)RoleMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vinjcent.mapper.RoleMapper">

    <!--基础字段映射-->
    <resultMap id="RoleResultMap" type="Role">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="nameZh" column="nameZh" />
    </resultMap>

    <!--根据用户id查询角色信息-->
    <select id="queryRolesByUid" resultMap="RoleResultMap">
        select r.id id,
               r.name name,
               r.nameZh nameZh
        from role r,
             user_role ur
        where r.id = ur.rid
          and ur.uid = #{uid}
    </select>

</mapper>

3)MenuMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybtais.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vinjcent.mapper.MenuMapper">

    <resultMap id="MenuResultMap" type="Menu">
        <id column="id" property="id" />
        <result column="pattern" property="pattern" />
        <collection property="roles" ofType="Role">
            <id column="rid" property="id" />
            <result column="rname" property="name" />
            <result column="rnameZh" property="nameZh" />
        </collection>
    </resultMap>

    <select id="getAllMenus" resultMap="MenuResultMap">
        select
            m.id id,
            m.pattern pattern,
            r.id rid,
            r.name rname,
            r.nameZh rnameZh
        from menu m
             left join menu_role mr
                       on m.id = mr.mid
             left join role r
                       on mr.rid = r.id
   </select>

</mapper>

[ Note ] The service layer code is too simple to give here, it can be implemented according to the mapper interface method

  • Test interface HelloController
package com.vinjcent.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    
    

    /**
     * 资源          可访问的角色
     * /admin/**    ROLE_ADMIN
     * /user/**     ROLE_ADMIN ROLE_USER
     * /guest/**    ROLE_ADMIN ROLE_USER ROLE_GUEST
     *
     *
     * 用户          具有的角色信息
     * admin        ADMIN USER GUEST
     * user         USER GUEST
     * vinjcent     GUEST
     *
     */

    @GetMapping("/admin/hello")
    public String admin() {
    
    
        return "hello admin";
    }

    @GetMapping("/user/hello")
    public String user() {
    
    
        return "hello user";
    }

    @GetMapping("/guest/hello")
    public String guest() {
    
    
        return "hello guest";
    }

    @GetMapping("/hello")
    public String hello() {
    
    
        return "hello";
    }
}
  • Authentication data source UserDetailsService
package com.vinjcent.config.security.service;

import com.vinjcent.pojo.Role;
import com.vinjcent.pojo.User;
import com.vinjcent.service.RoleService;
import com.vinjcent.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.List;

@Component
public class DivUserDetailsService implements UserDetailsService {
    
    

    // dao ===> springboot + mybatis
    private final UserService userService;

    private final RoleService roleService;

    @Autowired
    public DivUserDetailsService(UserService userService, RoleService roleService) {
    
    
        this.userService = userService;
        this.roleService = roleService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
    
        // 1.查询用户
        User user = userService.queryUserByUsername(username);
        if (ObjectUtils.isEmpty(user)) throw new UsernameNotFoundException("用户名不存在!");
        // 2.查询权限信息
        List<Role> roles = roleService.queryRolesByUid(user.getId());
        user.setRoles(roles);
        return user;
    }
}
  • Data source for custom url permission metadata FilterInvocationSecurityMetadataSource
package com.vinjcent.config.security.meta;

import com.vinjcent.pojo.Menu;
import com.vinjcent.pojo.Role;
import com.vinjcent.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;

/**
 * 自定义 url 权限元数据的数据源
 */
@Component
public class DivSecurityMetaSource implements FilterInvocationSecurityMetadataSource {
    
    


    private final MenuService menuService;

    @Autowired
    public DivSecurityMetaSource(MenuService menuService) {
    
    
        this.menuService = menuService;
    }

    // 用于路径对比
    AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * 自定义动态资源权限数据源信息
     * @param object
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    
    
        // 1.获取请求资源路径
        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();
        // 2.获取数据库中所有菜单
        List<Menu> menus = menuService.getAllMenus();
        // 3.循环对比数据库中的路径与当前uri是否匹配
        for (Menu menu : menus) {
    
    
            // 如果匹配成功
            if (antPathMatcher.match(menu.getPattern(), requestURI)) {
    
    
                // 转链表为数组
                String[] roles = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
                // 返回角色集合
                return SecurityConfig.createList(roles);
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    
    
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}
  • Custom security configuration class WebSecurityConfigurerAdapter
package com.vinjcent.config.security;

import com.vinjcent.config.security.meta.DivSecurityMetaSource;
import com.vinjcent.config.security.service.DivUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * 自定义 spring security 配置类
 */
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    

    // 注入认证数据源
    private final DivUserDetailsService userDetailsService;

    // 注入url元数据数据源
    private final DivSecurityMetaSource securityMetaSource;

    @Autowired
    public WebSecurityConfiguration(DivUserDetailsService userDetailsService, DivSecurityMetaSource securityMetaSource) {
    
    
        this.userDetailsService = userDetailsService;
        this.securityMetaSource = securityMetaSource;
    }

    // 替换认证数据源
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        auth.userDetailsService(userDetailsService);
    }

    // 暴露AuthenticationManager,使得这个bean能在组件中进行注入
    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
    
    
        return super.authenticationManager();
    }

    // http 拦截
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        // 1.获取工厂对象
        ApplicationContext context = http.getSharedObject(ApplicationContext.class);
        // 2.设置自定义 url 权限元数据
        http.apply(new UrlAuthorizationConfigurer<>(context))
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    
    
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
    
    
                        // 配置元数据信息
                        object.setSecurityMetadataSource(securityMetaSource);
                        // 是否拒绝公共资源的访问,设置为true,不允许访问公共资源
                        object.setRejectPublicInvocations(false);
                        return object;
                    }
                });

        // 开启表单验证
        http.formLogin();

        // 关闭csrf
        http.csrf().disable();
    }

}
  • Test the corresponding interface

Summarize

The corresponding role of the user has the specified permission access, the admin user has the ADMIN, USER, and GUEST roles, the user user has the USER, GUEST roles, and the vinjcent has the GUEST role. It is found /hellothat any role is allowed to access, /admin/helloonly ADMIN role is allowed to access; /user/helloit can be both USER role and ADMIN role; and /guest/helloonly ADMIN, USER, GUEST roles are allowed to access

Resources accessible by roles

/**
 * 资源          可访问的角色
 * /admin/**    ROLE_ADMIN
 * /user/**     ROLE_ADMIN ROLE_USER
 * /guest/**    ROLE_ADMIN ROLE_USER ROLE_GUEST
 */

Guess you like

Origin blog.csdn.net/Wei_Naijia/article/details/128687789