A nova versão do Spring Security é elegante o suficiente para usar!
A nova versão do Spring Security é elegante o suficiente para usar!
Não faz muito tempo, o Spring Boot 2.7.0 foi lançado, e o Spring Security também foi atualizado para a versão 5.7.1. Após a atualização, descobri que o método de configuração do Spring Security que eu vinha usando foi descontinuado. Não posso deixar de sentir que a evolução tecnológica é tão rápida — mal começamos a usar algo e já se torna obsoleto! Hoje, vou mostrar o uso mais recente do Spring Security para ver se é elegante o suficiente!
Projeto prático de e-commerce com Spring Boot — mall (50k+ estrelas): github.com/macrozheng/…
Vamos primeiro comparar a funcionalidade básica de autenticação de login fornecida pelo Spring Security para ver se a nova versão é melhor.
Primeiro, modifique o arquivo pom.xml do projeto e atualize a versão do Spring Boot para 2.7.0.
org.springframework.boot
spring-boot-starter-parent
2.7.0
Em versões anteriores ao Spring Boot 2.7.0, era necessário criar uma classe de configuração que herdasse WebSecurityConfigurerAdapter e depois sobrescrever os três métodos do Adapter para configuração;
/**
* Configuração do SpringSecurity
* Criado por macro em 26/04/2018.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//Omitir a configuração do HttpSecurity
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Se você usar no Spring Boot versão 2.7.0, descobrirá que o WebSecurityConfigurerAdapter foi descontinuado, e parece que o Spring Security decidiu abandonar definitivamente esse uso!
Novo uso
O novo uso é muito simples: não é mais necessário herdar WebSecurityConfigurerAdapter. Basta declarar a classe de configuração diretamente, configurar um método para gerar o SecurityFilterChainBean e mover a configuração original do HttpSecurity para esse método.
/**
* Nova configuração de uso para SpringSecurity 5.4.x e superior
* Para evitar dependências circulares, usado apenas para configurar HttpSecurity
* Criado por macro em 19/05/2022.
*/
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omitir a configuração do HttpSecurity
return httpSecurity.build();
}
}
O novo uso parece muito simples e direto, evitando a necessidade de herdar WebSecurityConfigurerAdapter e sobrescrever métodos. É altamente recomendável que você faça a atualização!
Após a atualização para o Spring Boot 2.7.0, o Spring Security fez grandes alterações no método de configuração. Isso tem algum impacto em outros usos? Na verdade, não há impacto. Vamos falar sobre como usar o Spring Security para implementar controle de permissões dinâmicas!
Primeiro, vamos falar sobre o controle de permissões dinâmicas baseado em método. Embora esse método seja simples de implementar, ele apresenta certas desvantagens.
Use @EnableGlobalMethodSecurity na classe de configuração para ativá-lo;
/**
* Configuração do SpringSecurity
* Criado por macro em 26/04/2018.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
}
Em seguida, use @PreAuthorize no método para configurar as permissões necessárias para acessar a interface;
/**
* Controlador de gerenciamento de produtos
* Criado por macro em 26/04/2018.
*/
@Controller
@Api(tags = "PmsProductController", description = "Gerenciamento de Produtos")
@RequestMapping("/product")
public class PmsProductController {
@Autowired
private PmsProductService productService;
@ApiOperation("Criar um item")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
@PreAuthorize("hasAuthority('pms:product:create')")
public CommonResult create(@RequestBody PmsProductParam productParam, BindingResult bindingResult) {
int count = productService.create(productParam);
if (count > 0) {
return CommonResult.success(count);
} else {
return CommonResult.failed();
}
}
}
Depois, consulte o valor de permissão do usuário no banco de dados e defina-o no objeto UserDetails. Embora esse método seja conveniente de implementar, não é elegante escrever o valor de permissão diretamente no método.
/**
* Classe de implementação do UmsAdminService
* Criado por macro em 26/04/2018.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public UserDetails loadUserByUsername(String username){
//obter informações do usuário
UmsAdmin admin = getAdminByUsername(username);
if (admin != null) {
List permissionList = getPermissionList(admin.getId());
return new AdminUserDetails(admin,permissionList);
}
throw new UsernameNotFoundException("Nome de usuário ou senha incorretos");
}
}
Na verdade, o caminho correspondente a cada interface é único, e é mais elegante controlar as permissões da interface por meio do caminho.
Primeiro, precisamos criar um filtro para permissões dinâmicas. Preste atenção ao método doFilter aqui, que é usado para configurar e liberar requisições OPTIONS e da lista branca. Ele chamará o método super.beforeInvocation(fi), que por sua vez chamará o método decide no AccessDecisionManager para realizar a autenticação;
/**
* Filtro de segurança dinâmica para filtragem de permissões dinâmicas baseadas em caminho
* Criado por macro em 07/02/2020.
*/
public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
@Autowired
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {
super.setAccessDecisionManager(dynamicAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
//Requisições OPTIONS são liberadas diretamente
if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
//Requisições da lista branca são liberadas diretamente
PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) {
if(pathMatcher.match(path,request.getRequestURI())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
}
//Aqui será chamado o método decide no AccessDecisionManager para operação de autenticação
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return dynamicSecurityMetadataSource;
}
}
Em seguida, precisamos criar uma classe que herde AccessDecisionManager e usar o método decide para comparar as permissões exigidas pela interface de acesso com as permissões que o usuário possui; se houver correspondência, o acesso será liberado;
/**
* Gerenciador de decisão de permissões dinâmicas, usado para determinar se o usuário tem direitos de acesso
* Criado por macro em 07/02/2020.
*/
public class DynamicAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
// Liberar diretamente quando a interface não tiver recursos configurados
if (CollUtil.isEmpty(configAttributes)) {
return;
}
Iterator iterator = configAttributes.iterator();
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
// Comparar acesso aos recursos necessários ou recursos pertencentes ao usuário
String needAuthority = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("Desculpe, você não tem permissão de acesso");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
Como o atributo configAttributes no método decide acima é obtido pelo método getAttributes do FilterInvocationSecurityMetadataSource, também precisamos criar uma classe que o herde, e o método getAttributes pode ser usado para obter o valor de permissão necessário para acessar o caminho atual;
/**
* Fonte de dados de permissões dinâmicas para obtenção de regras de permissões dinâmicas
* Criado por macro em 07/02/2020.
*/
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Map configAttributeMap = null;
@Autowired
private DynamicSecurityService dynamicSecurityService;
@PostConstruct
public void loadDataSource() {
configAttributeMap = dynamicSecurityService.loadDataSource();
}
public void clearDataSource() {
configAttributeMap.clear();
configAttributeMap = null;
}
@Override
public Collection getAttributes(Object o) throws IllegalArgumentException {
if (configAttributeMap == null) this.loadDataSource();
List configAttributes = new ArrayList<>();
//Obter o caminho acessado atualmente
String url = ((FilterInvocation) o).getRequestUrl();
String path = URLUtil.getPath(url);
PathMatcher pathMatcher = new AntPathMatcher();
Iterator iterator = configAttributeMap.keySet().iterator();
//Obter os recursos necessários para acessar o caminho
while (iterator.hasNext()) {
String pattern = iterator.next();
if (pathMatcher.match(pattern, path)) {
configAttributes.add(configAttributeMap.get(pattern));
}
}
// A permissão de requisição de operação não está definida, retornar coleção vazia
return configAttributes;
}
@Override
public Collection getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
É importante notar aqui que os dados de valores de permissão correspondentes a todos os caminhos vêm do DynamicSecurityService personalizado;
/**
* Classe de negócios relacionada a permissões dinâmicas
* Criado por macro em 07/02/2020.
*/
public interface DynamicSecurityService {
/**
* Carregar MAP de curingas ANT de recursos e recursos correspondentes
*/
Map loadDataSource();
}
Com tudo pronto, adicione o filtro de permissões dinâmicas antes do FilterSecurityInterceptor;
/**
* Nova configuração de uso para SpringSecurity 5.4.x e superior
* Para evitar dependências circulares, usado apenas para configurar HttpSecurity
* Criado por macro em 19/05/2022.
*/
@Configuration
public class SecurityConfig {
@Autowired
private DynamicSecurityService dynamicSecurityService;
@Autowired
private DynamicSecurityFilter dynamicSecurityFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omitir parte da configuração...
//Adicionar filtro de verificação de permissões dinâmicas quando houver configuração de permissões dinâmicas
if(dynamicSecurityService!=null){
registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
}
return httpSecurity.build();
}
}
Se você leu este artigo, são necessários apenas quatro passos para integrar SpringSecurity+JWT e implementar autenticação de login! Nesse caso, você sabe que deve configurar esses dois beans: um responsável por obter as informações do usuário conectado e outro responsável por obter as regras de permissões dinâmicas armazenadas. Para se adaptar ao novo uso do Spring Security, não herdamos mais SecurityConfig, o que é muito mais simples!
/**
* Configurações relacionadas ao módulo mall-security
* Configuração personalizada para definir como obter informações do usuário e permissões dinâmicas
* Criado por macro em 20/05/2022.
*/
@Configuration
public class MallSecurityConfig {
@Autowired
private UmsAdminService adminService;
@Bean
public UserDetailsService userDetailsService() {
//Obter informações do usuário conectado
return username -> {
AdminUserDetails admin = adminService.getAdminByUsername(username);
if (admin != null) {
return admin;
}
throw new UsernameNotFoundException("Nome de usuário ou senha incorretos");
};
}
@Bean
public DynamicSecurityService dynamicSecurityService() {
return new DynamicSecurityService() {
@Override
public Map loadDataSource() {
Map map = new ConcurrentHashMap<>();
List resourceList = adminService.getResourceList();
for (UmsResource resource : resourceList) {
map.put(resource.getUrl(), new org.springframework.security.access.SecurityConfig(resource.getId() + ":" + resource.getName()));
}
return map;
}
};
}
}
Teste de efeito
Em seguida, inicie nosso projeto de exemplo mall-tiny-security, faça login com a seguinte conta e senha. Esta conta está configurada apenas para acessar /brand/listAll, e o endereço de acesso é: http://localhost:8088/swagger-ui/
Depois, coloque o token retornado no cabeçalho de autenticação do Swagger;
Ao acessarmos a interface autorizada, os dados podem ser obtidos normalmente;
Ao acessarmos a interface sem permissão, é retornada a mensagem indicando que não há permissão de acesso.
O uso atualizado do Spring Security é realmente elegante o suficiente, simples o suficiente e mais compatível com o uso anterior! Pessoalmente, acredito que um framework maduro não altera drasticamente seu modo de uso durante o processo de atualização. Mesmo quando há mudanças, mantém compatibilidade com o uso anterior. Portanto, para a maioria dos frameworks, quem já usava a versão antiga conseguirá usar a nova versão sem problemas!
Não faz muito tempo, o Spring Boot 2.7.0 foi lançado, e o Spring Security também foi atualizado para a versão 5.7.1. Após a atualização, descobri que o método de configuração do Spring Security que eu vinha usando foi descontinuado. Não posso deixar de sentir que a evolução tecnológica é tão rápida — mal começamos a usar algo e já se torna obsoleto! Hoje, vou mostrar o uso mais recente do Spring Security para ver se é elegante o suficiente!
Projeto prático de e-commerce com Spring Boot — mall (50k+ estrelas): github.com/macrozheng/…
Uso básico
Vamos primeiro comparar a funcionalidade básica de autenticação de login fornecida pelo Spring Security para ver se a nova versão é melhor.
Versão atualizada
Primeiro, modifique o arquivo pom.xml do projeto e atualize a versão do Spring Boot para 2.7.0.
Uso antigo
Em versões anteriores ao Spring Boot 2.7.0, era necessário criar uma classe de configuração que herdasse WebSecurityConfigurerAdapter e depois sobrescrever os três métodos do Adapter para configuração;
/**
* Configuração do SpringSecurity
* Criado por macro em 26/04/2018.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//Omitir a configuração do HttpSecurity
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Se você usar no Spring Boot versão 2.7.0, descobrirá que o WebSecurityConfigurerAdapter foi descontinuado, e parece que o Spring Security decidiu abandonar definitivamente esse uso!
Novo uso
O novo uso é muito simples: não é mais necessário herdar WebSecurityConfigurerAdapter. Basta declarar a classe de configuração diretamente, configurar um método para gerar o SecurityFilterChainBean e mover a configuração original do HttpSecurity para esse método.
/**
* Nova configuração de uso para SpringSecurity 5.4.x e superior
* Para evitar dependências circulares, usado apenas para configurar HttpSecurity
* Criado por macro em 19/05/2022.
*/
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omitir a configuração do HttpSecurity
return httpSecurity.build();
}
}
O novo uso parece muito simples e direto, evitando a necessidade de herdar WebSecurityConfigurerAdapter e sobrescrever métodos. É altamente recomendável que você faça a atualização!
Uso avançado
Após a atualização para o Spring Boot 2.7.0, o Spring Security fez grandes alterações no método de configuração. Isso tem algum impacto em outros usos? Na verdade, não há impacto. Vamos falar sobre como usar o Spring Security para implementar controle de permissões dinâmicas!
Permissões dinâmicas baseadas em método
Primeiro, vamos falar sobre o controle de permissões dinâmicas baseado em método. Embora esse método seja simples de implementar, ele apresenta certas desvantagens.
Use @EnableGlobalMethodSecurity na classe de configuração para ativá-lo;
/**
* Configuração do SpringSecurity
* Criado por macro em 26/04/2018.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
}
Em seguida, use @PreAuthorize no método para configurar as permissões necessárias para acessar a interface;
/**
* Controlador de gerenciamento de produtos
* Criado por macro em 26/04/2018.
*/
@Controller
@Api(tags = "PmsProductController", description = "Gerenciamento de Produtos")
@RequestMapping("/product")
public class PmsProductController {
@Autowired
private PmsProductService productService;
@ApiOperation("Criar um item")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
@PreAuthorize("hasAuthority('pms:product:create')")
public CommonResult create(@RequestBody PmsProductParam productParam, BindingResult bindingResult) {
int count = productService.create(productParam);
if (count > 0) {
return CommonResult.success(count);
} else {
return CommonResult.failed();
}
}
}
Depois, consulte o valor de permissão do usuário no banco de dados e defina-o no objeto UserDetails. Embora esse método seja conveniente de implementar, não é elegante escrever o valor de permissão diretamente no método.
/**
* Classe de implementação do UmsAdminService
* Criado por macro em 26/04/2018.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public UserDetails loadUserByUsername(String username){
//obter informações do usuário
UmsAdmin admin = getAdminByUsername(username);
if (admin != null) {
List
return new AdminUserDetails(admin,permissionList);
}
throw new UsernameNotFoundException("Nome de usuário ou senha incorretos");
}
}
Permissões dinâmicas baseadas em caminho
Na verdade, o caminho correspondente a cada interface é único, e é mais elegante controlar as permissões da interface por meio do caminho.
Primeiro, precisamos criar um filtro para permissões dinâmicas. Preste atenção ao método doFilter aqui, que é usado para configurar e liberar requisições OPTIONS e da lista branca. Ele chamará o método super.beforeInvocation(fi), que por sua vez chamará o método decide no AccessDecisionManager para realizar a autenticação;
/**
* Filtro de segurança dinâmica para filtragem de permissões dinâmicas baseadas em caminho
* Criado por macro em 07/02/2020.
*/
public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
@Autowired
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {
super.setAccessDecisionManager(dynamicAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
//Requisições OPTIONS são liberadas diretamente
if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
//Requisições da lista branca são liberadas diretamente
PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) {
if(pathMatcher.match(path,request.getRequestURI())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
}
//Aqui será chamado o método decide no AccessDecisionManager para operação de autenticação
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return dynamicSecurityMetadataSource;
}
}
Em seguida, precisamos criar uma classe que herde AccessDecisionManager e usar o método decide para comparar as permissões exigidas pela interface de acesso com as permissões que o usuário possui; se houver correspondência, o acesso será liberado;
/**
* Gerenciador de decisão de permissões dinâmicas, usado para determinar se o usuário tem direitos de acesso
* Criado por macro em 07/02/2020.
*/
public class DynamicAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection
// Liberar diretamente quando a interface não tiver recursos configurados
if (CollUtil.isEmpty(configAttributes)) {
return;
}
Iterator
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
// Comparar acesso aos recursos necessários ou recursos pertencentes ao usuário
String needAuthority = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("Desculpe, você não tem permissão de acesso");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
Como o atributo configAttributes no método decide acima é obtido pelo método getAttributes do FilterInvocationSecurityMetadataSource, também precisamos criar uma classe que o herde, e o método getAttributes pode ser usado para obter o valor de permissão necessário para acessar o caminho atual;
/**
* Fonte de dados de permissões dinâmicas para obtenção de regras de permissões dinâmicas
* Criado por macro em 07/02/2020.
*/
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Map
@Autowired
private DynamicSecurityService dynamicSecurityService;
@PostConstruct
public void loadDataSource() {
configAttributeMap = dynamicSecurityService.loadDataSource();
}
public void clearDataSource() {
configAttributeMap.clear();
configAttributeMap = null;
}
@Override
public Collection
if (configAttributeMap == null) this.loadDataSource();
List
//Obter o caminho acessado atualmente
String url = ((FilterInvocation) o).getRequestUrl();
String path = URLUtil.getPath(url);
PathMatcher pathMatcher = new AntPathMatcher();
Iterator
//Obter os recursos necessários para acessar o caminho
while (iterator.hasNext()) {
String pattern = iterator.next();
if (pathMatcher.match(pattern, path)) {
configAttributes.add(configAttributeMap.get(pattern));
}
}
// A permissão de requisição de operação não está definida, retornar coleção vazia
return configAttributes;
}
@Override
public Collection
return null;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
É importante notar aqui que os dados de valores de permissão correspondentes a todos os caminhos vêm do DynamicSecurityService personalizado;
/**
* Classe de negócios relacionada a permissões dinâmicas
* Criado por macro em 07/02/2020.
*/
public interface DynamicSecurityService {
/**
* Carregar MAP de curingas ANT de recursos e recursos correspondentes
*/
Map
}
Com tudo pronto, adicione o filtro de permissões dinâmicas antes do FilterSecurityInterceptor;
/**
* Nova configuração de uso para SpringSecurity 5.4.x e superior
* Para evitar dependências circulares, usado apenas para configurar HttpSecurity
* Criado por macro em 19/05/2022.
*/
@Configuration
public class SecurityConfig {
@Autowired
private DynamicSecurityService dynamicSecurityService;
@Autowired
private DynamicSecurityFilter dynamicSecurityFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omitir parte da configuração...
//Adicionar filtro de verificação de permissões dinâmicas quando houver configuração de permissões dinâmicas
if(dynamicSecurityService!=null){
registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
}
return httpSecurity.build();
}
}
Se você leu este artigo, são necessários apenas quatro passos para integrar SpringSecurity+JWT e implementar autenticação de login! Nesse caso, você sabe que deve configurar esses dois beans: um responsável por obter as informações do usuário conectado e outro responsável por obter as regras de permissões dinâmicas armazenadas. Para se adaptar ao novo uso do Spring Security, não herdamos mais SecurityConfig, o que é muito mais simples!
/**
* Configurações relacionadas ao módulo mall-security
* Configuração personalizada para definir como obter informações do usuário e permissões dinâmicas
* Criado por macro em 20/05/2022.
*/
@Configuration
public class MallSecurityConfig {
@Autowired
private UmsAdminService adminService;
@Bean
public UserDetailsService userDetailsService() {
//Obter informações do usuário conectado
return username -> {
AdminUserDetails admin = adminService.getAdminByUsername(username);
if (admin != null) {
return admin;
}
throw new UsernameNotFoundException("Nome de usuário ou senha incorretos");
};
}
@Bean
public DynamicSecurityService dynamicSecurityService() {
return new DynamicSecurityService() {
@Override
public Map
Map
List
for (UmsResource resource : resourceList) {
map.put(resource.getUrl(), new org.springframework.security.access.SecurityConfig(resource.getId() + ":" + resource.getName()));
}
return map;
}
};
}
}
Teste de efeito
Em seguida, inicie nosso projeto de exemplo mall-tiny-security, faça login com a seguinte conta e senha. Esta conta está configurada apenas para acessar /brand/listAll, e o endereço de acesso é: http://localhost:8088/swagger-ui/
Depois, coloque o token retornado no cabeçalho de autenticação do Swagger;
Ao acessarmos a interface autorizada, os dados podem ser obtidos normalmente;
Ao acessarmos a interface sem permissão, é retornada a mensagem indicando que não há permissão de acesso.
Resumo
O uso atualizado do Spring Security é realmente elegante o suficiente, simples o suficiente e mais compatível com o uso anterior! Pessoalmente, acredito que um framework maduro não altera drasticamente seu modo de uso durante o processo de atualização. Mesmo quando há mudanças, mantém compatibilidade com o uso anterior. Portanto, para a maioria dos frameworks, quem já usava a versão antiga conseguirá usar a nova versão sem problemas!
Artigos relacionados
-
6 tecnologias opcionais para armazenamento de dados
Equipe da base de conhecimento
Explorar mais ofertas especiais
-
Short Message Service (SMS) e Serviço de E-mail
Pacote de 50.000 e-mails a partir de US$ 1,99; 120 mensagens curtas a partir de apenas US$ 1,00
