我正在为我的电子商务网站开发 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 日):UserDetailsServiceImpl 如下。
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/