url-based dynamic permissions

Keywords: Programming Database Spring

Solution 1: Extend the SpEL expression of access()

.anyRequest().access("@authService.canAcess(request,authentication)")

Solution 2: Customize Access Decision Manager

(1) Customize Security Metadata Source to load ConfigAttribute from database

(2) Customize access decision manager to verify permissions

(3) Customize Security Metadata Source and AccessDecision Manager using withObjectPostProcessor

Solution 3: Custom Filter

spring security is made up of a lot of filters, so we can customize filters and add them to fiterChain.

(1) Authentication data rules data source data need to be provided: through the implementation of the interface FilterInvocation Security Metadata Source to achieve.

(2) Custom Access Decision Manager: Perform permission validation

(3) Customize an interceptor and add it to fiterChain of spring security

Here we use scheme 1.

I. Creating the Entity Class Permission

@Entity
public class Permission {
    @Id @GeneratedValue
    private long id; // Primary key

    private  String name; // Permission name

    private String description; // Permission name

    /**
     * Note: The ulr wildcard of the Permission table is two stars, for example, all URLs under / user should be written as / user/**
     */
    private String url; // Authorized connection

    private Long pid; // Parent node

    // Role-authority is a many-to-many relationship
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "RolePermission",joinColumns = {@JoinColumn(name="permission_id")},inverseJoinColumns = {@JoinColumn(name="role_id")})
    private List<Role> roles;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

Establishment of Role Repository

public interface RoleRepository extends JpaRepository<Role,Long> {
}

3. Creating service classes

public interface PermissionService {
    Map<String, Collection<ConfigAttribute>> getPermissionMap ();
}

@Service
public class PermissionServiceImpl implements PermissionService {

    @Autowired
    private PermissionRepository permissionRepository;

    private Map<String, Collection<ConfigAttribute>> permissionMap = null;

    @PostConstruct
    /**
     * Get all the privilege information from the database, then traverse it and store it in the permissionMap collection
     */
    public void initPermissions() {
        permissionMap = new HashMap<>();
        List<Permission> permissions = permissionRepository.findAll();
        for (Permission p : permissions) {
            Collection<ConfigAttribute> collection = new ArrayList<ConfigAttribute>();
            for (Role role : p.getRoles()) {
                ConfigAttribute configAttribute = new SecurityConfig("ROLE_"+role.getName());
                collection.add(configAttribute);
            }
            permissionMap.put(p.getUrl(),collection);
        }
        System.out.println(permissionMap);
    }

    @Override
    public Map<String,Collection<ConfigAttribute>> getPermissionMap (){
        if(permissionMap == null || permissionMap.size() == 0){
            initPermissions();
        }
        return permissionMap;
    }
}

IV. Initialization data

@Service
public class DataInit {
    @Autowired
    private UserInfoRepository userInfoRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private PermissionRepository permissionRepository;

    @PostConstruct
    public void dataInit(){
        // role
        List<Role> roles = new ArrayList<>();
        Role adminRole = new Role();
        adminRole.setName("admin");
        adminRole.setDescprtion("Administrators");
        roleRepository.save(adminRole);
        roles.add(adminRole);

        Role userRole = new Role();
        userRole.setName("normal");
        userRole.setDescprtion("Ordinary users");
        roleRepository.save(userRole);
        roles.add(userRole);

        Permission userPermission = new Permission();
        userPermission.setName("Ordinary user's url");
        userPermission.setDescription("Allow ordinary user access");
        userPermission.setUrl("/hello/helloUser");
        userPermission.setRoles(roles);
        permissionRepository.save(userPermission);

        Permission adminPermission = new Permission();
        adminPermission.setName("Administrator url");
        adminPermission.setDescription("Allow Administrator Access");
        adminPermission.setUrl("/hello/helloAdmin");
        roles = new ArrayList<>();
        roles.add(adminRole);
        adminPermission.setRoles(roles);
        permissionRepository.save(adminPermission);

        // admin
        UserInfo admin = new UserInfo();
        admin.setUsername("admin");
        admin.setPassword(passwordEncoder.encode("123"));
        admin.setRoles(roles);
        userInfoRepository.save(admin);

        // user
        roles = new ArrayList<>();
        roles.add(userRole);
        UserInfo user = new UserInfo();
        user.setUsername("user");
        user.setPassword(passwordEncoder.encode("123"));
        user.setRoles(roles);
        userInfoRepository.save(user);
    }
}

5. Dynamic access and matching based on url

@Service
public class AuthService {
    @Autowired
    private PermissionService permissionService;
    public boolean canAcess(HttpServletRequest request, Authentication authentication) {
        boolean b = false;

        String url = request.getRequestURI();

        /**
         * 1,If you are not logged in, you need to make a judgment or intercept.
         */Object principal = authentication.getPrincipal();
        if(principal == null || "anonymousUser".equals(principal)) {
          return b;
        }

        /**
         * 2,Anonymous role ROLE_ANONYMOUS
         */
         if(authentication instanceof AnonymousAuthenticationToken) {
             // Anonymous role
             // check
             // return
         }

        /**
         * 3,Access permission information through requst object url
         */
        Map<String, Collection<ConfigAttribute>> map = permissionService.getPermissionMap();
        Collection<ConfigAttribute>  configAttributes = null;
        for (Iterator<String> it = map.keySet().iterator();it.hasNext();) {
            String curUrl = it.next();
            AntPathRequestMatcher matcher = new AntPathRequestMatcher(curUrl);
            if (matcher.matches((request))) {
                configAttributes = map.get(curUrl);
                break;
            }
        }

        if(configAttributes == null || configAttributes.size() == 0) {
            return b;
        }

        /**
         * 4,Compare the access information obtained with the access information of the current login account.
         */
        for(Iterator<ConfigAttribute> it = configAttributes.iterator();it.hasNext();){
            ConfigAttribute cfa = it.next();
            String role = cfa.getAttribute();
            for(GrantedAuthority authority : authentication.getAuthorities()){
                if(role.equals(authority.getAuthority())){
                    b = true;
                    break;
                }
            }
        }

        return b;
    }
}

6. Use. anyRequest().access()

@Override
protected void configure (HttpSecurity http) throws Exception{
   http.formLogin().loginPage("/login")
   .and()
   .authorizeRequests()
           .antMatchers("/login").permitAll() //Allow everyone access to the landing page
    .antMatchers("/","/index").permitAll()
           .antMatchers("/test/**","/test1/**","/favicon.ico").permitAll()
           .antMatchers("/res/**/*.{js,html}").permitAll()

   .anyRequest().access("@authService.canAcess(request,authentication)")
   .and().sessionManagement().maximumSessions(1);
   //anyRequest().authenticated(); // All requests need to be accessed after login
}

Problems encountered: 1. After successful landing, there is always no real right to visit. Here, first set the home page as visible.

.antMatchers("/","/index").permitAll()

2. After successful login, always visit / favicon.ico, which leads to the mismatch of permissions. The solution is to add a / favicon.ico image and call it on the page in use. There are better ways. https://www.jianshu.com/p/b56e524ba2e8 https://blog.csdn.net/sdjadycsdn/article/details/82621234

Posted by ssidellq on Wed, 09 Oct 2019 01:17:04 -0700