我正在为我的电子商务网站开发 RBAC 模型。在这种情况下,将有多个客户注册到我的网站。对于每个客户端,都会创建不同的数据库。

对于每个数据库。将有用户、角色和权限表。将有一个管理员可以添加用户并为用户分配角色。帐户的管理员也可以添加角色和修改角色。一旦客户端从面板更新,就应该完成此更改。并会在用户重新登录面板后反射(reflect)出来。

现在 Spring boot 安全配置在服务器启动时加载。如何将安全性应用于用户运行时。

我在本地创建了一个 RBAC 项目。为用户、角色和权限创建模型。我有一个 SecurityConfig 类定义系统中用户的安全性。代码如下

package com.rhv.config;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.rhv.um.model.Role;
import com.rhv.um.service.RoleService;
import com.rhv.util.Utill;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private RoleService roleService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/registration/**").permitAll();

        http.authorizeRequests()
            .antMatchers(HttpMethod.GET, "/um/roles", "/um/users/{\\d+}",
                    "/um/users", "/um/permission").hasAuthority("Administrator");

        http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login").permitAll()
            .and()
            .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/login").permitAll();
    }

    @Bean
    public AuthenticationManager customAuthenticationManager() throws Exception {
        return authenticationManager();
    }

    @Autowired
    private void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

在这个我想要来自数据库的角色和权限。如果用户已登录,则应自动加载用户的安全性。如果管理员的角色或权限有任何更改。一旦用户重新登录应用程序,同样应该反射(reflect)给用户。

编辑(2019 年 9 月 19 日):UserDetailsS​​erviceImpl 如下。
package com.rhv.um.service.impl;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Service;
import org.springframework.transaction.annotation.Transactional;

import com.rhv.um.model.Role;
import com.rhv.um.model.User;
import com.rhv.um.service.UserService;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserService userService;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null)
            throw new UsernameNotFoundException("Username does not exists: " + username);
        Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
        for (Role role : user.getRoles()) {
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
        }

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                grantedAuthorities);
    }

}

我添加了 UserService 类,它从数据库中获取 User 对象。用户对象与角色有关系。角色与权限有关系。请找到 UserServiceImpl 代码如下
package com.rhv.um.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.rhv.um.model.User;
import com.rhv.um.repository.UserRepository;
import com.rhv.um.service.UserService;

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    UserRepository userRepository;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public void save(User user) {
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        userRepository.save(user);
    }
    @Override
    public List<User> findAll() {
        return userRepository.findAll();
    }
    @Override
    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }
}

最佳答案

我已经得到了这个问题的解决方案。我必须实现 spring 的 FilterInvocationSecurityMetadataSource 和 AccessDecisionManager

下面是我的 FilterInvocationSecurityMetadataSource,它返回允许/拒绝 ConfigAttribute。

package com.rhv.um.filter;

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

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpMethod;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.rhv.RegistrationApplication;

public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    public List<ConfigAttribute> getAttributes(Object object) {
        FilterInvocation fi = (FilterInvocation) object;
        HttpServletRequest request = fi.getRequest();
        HttpMethod httpMethod = HttpMethod.valueOf(fi.getRequest().getMethod());

        // Bypassing Security check for /js, /css and /images url
        if (new AntPathRequestMatcher("/js/**").matches(request)
                || new AntPathRequestMatcher("/css/**").matches(request)
                || new AntPathRequestMatcher("/images/**").matches(request)
                || new AntPathRequestMatcher("/login").matches(request)
                || new AntPathRequestMatcher("/").matches(request)) {
            return SecurityConfig.createList(new String[] { "Allow" });
        }

        Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication()
                .getAuthorities();

        try {
            for (GrantedAuthority grantedAuthority : authorities) {
                if(grantedAuthority.toString().equalsIgnoreCase("Administrator")) {
                    return SecurityConfig.createList(new String[] { "Allow" });
                }

                for(String allowedUrl : RegistrationApplication.permissions.get(grantedAuthority.toString()).get(httpMethod)) {
                    if(new AntPathRequestMatcher(allowedUrl).matches(request)) {
                        return SecurityConfig.createList(new String[] { "Allow" });
                    }
                }
            }
        } catch (Exception e) {
            return SecurityConfig.createList(new String[] { "Deny" });
        }

        return SecurityConfig.createList(new String[] { "Deny" });
    }

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

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

MyFilterSecurityMetadataSource 的返回类型由实现 AccessDecisionManager 的 MyAccessDecisionManager 类使用。下面是 MyAccessDecisionManager 的代码
package com.rhv.um.filter;

import java.util.Collection;
import java.util.Iterator;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;

public class MyAccessDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        if (configAttributes == null || configAttributes.size() == 0) {
            return;
        }
        Iterator<ConfigAttribute> ite = configAttributes.iterator();
        if(ite.next().toString().equalsIgnoreCase("Allow")) {
            return;
        }
        else {
            throw new AccessDeniedException("Access is denied");
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return false;
    }

}

上面的代码将决定用户是否可以访问特定的 url。我已经使用允许/拒绝 ConfigAttribute 处理了它。

最后是我的 SecurityConfig 类代码,它提供了 WebSecurityConfigurerAdapter 的实现。下面是 SecurityConfig 的代码
package com.rhv.config;

import org.springframework.beans.factory.annotation.Autowired;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.rhv.um.filter.MyAccessDecisionManager;
import com.rhv.um.filter.MyFilterSecurityMetadataSource;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest().authenticated()
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                public <O extends FilterSecurityInterceptor> O postProcess(
                        O fsi) {
                    FilterInvocationSecurityMetadataSource newSource = new MyFilterSecurityMetadataSource();
                    fsi.setSecurityMetadataSource(newSource);
                    fsi.setAccessDecisionManager(new MyAccessDecisionManager());
                    return fsi;
                }
            })
            .and()
            .formLogin()
                .loginPage("/login").permitAll()
            .and()
            .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/login").permitAll();
    }

    @Bean
    public AuthenticationManager customAuthenticationManager() throws Exception {
        return authenticationManager();
    }

    @Autowired
    private void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

}

这个实现帮助我动态地允许用户访问 url。

关于java - 从屏幕修改时如何自动加载 Spring Security,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57991065/

10-12 13:59