Spring Security'nin yeni sürümü kullanım kolaylığı ve zarafetiyle öne çıkıyor.
Kısa süre önce Spring Boot 2.7.0 yayımlandı ve Spring Security de 5.7.1 sürümüne yükseltildi. Yükseltme sonrasında, uzun süredir kullandığım Spring Security yapılandırma yönteminin artık kullanımdan kaldırıldığını fark ettim. Teknoloji güncellemelerinin bu kadar hızlı olması ve kullanılan yöntemlerin hızla terk edilmesi şaşırtıcıdır. Bugün size Spring Security'nin en son kullanımını gösterecek ve ne kadar zarif olduğunu birlikte inceleyeceğiz.
SpringBoot gerçek dünya e-ticaret projesi mall (50 bin+ yıldız) adresi: github.com/macrozheng/…
Öncelikle Spring Security tarafından sağlanan temel işlev olan oturum açma kimlik doğrulamasını karşılaştırarak yeni sürümün daha iyi olup olmadığını görelim.
İlk olarak projenin pom.xml dosyasını değiştirin ve Spring Boot sürümünü 2.7.0'a yükseltin.
org.springframework.boot
spring-boot-starter-parent
2.7.0
Spring Boot 2.7.0 öncesindeki sürümlerde, WebSecurityConfigurerAdapter sınıfından miras alan bir yapılandırma sınıfı yazmamız ve ardından yapılandırma için Adapter içindeki üç yöntemi geçersiz kılmamız gerekiyordu;
/**
* SpringSecurity configuration
* Created by macro on 2018/4/26.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//Omit the configuration of HttpSecurity
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Bu kodu SpringBoot 2.7.0 sürümünde kullanırsanız WebSecurityConfigurerAdapter sınıfının kullanımdan kaldırıldığını görürsünüz; görünüşe göre Spring Security bu kullanımı kesin olarak terk ediyor.
Yeni kullanım
Yeni kullanım oldukça basittir; WebSecurityConfigurerAdapter sınıfından miras almanıza gerek yoktur. Doğrudan yapılandırma sınıfını bildirin, SecurityFilterChainBean oluşturmak için bir yöntem yapılandırın ve orijinal HttpSecurity yapılandırmasını bu yönteme taşıyın.
/**
* New usage configuration for SpringSecurity 5.4.x and above
* To avoid circular dependencies, only used to configure HttpSecurity
* Created by macro on 2022/5/19.
*/
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omit the configuration of HttpSecurity
return httpSecurity.build();
}
}
Yeni kullanım oldukça basit ve anlaşılırdır; WebSecurityConfigurerAdapter sınıfından miras alma ve yöntemi geçersiz kılma işlemini ortadan kaldırır. Bu güncellemeyi yapmanız şiddetle tavsiye edilir.
Spring Boot 2.7.0 sürümüne yükseltme yaptıktan sonra Spring Security, yapılandırma yönteminde büyük değişiklikler yaptı. Peki bu durum diğer kullanımları etkiliyor mu? Aslında hiçbir etkisi yok. Şimdi dinamik izin kontrolü uygulamak için Spring Security'nin nasıl kullanılacağını anlatalım.
Öncelikle yöntem tabanlı dinamik izin kontrolünden bahsedelim. Bu yöntemin uygulanması basit olsa da bazı dezavantajları vardır.
Etkinleştirmek için yapılandırma sınıfında @EnableGlobalMethodSecurity notunu kullanın;
/**
* SpringSecurity configuration
* Created by macro on 2018/4/26.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
}
Ardından arayüze erişmek için gereken izinleri yapılandırmak üzere yöntemde @PreAuthorize notunu kullanın;
/**
* Commodity Management Controller
* Created by macro on 2018/4/26.
*/
@Controller
@Api(tags = "PmsProductController", description = "Product Management")
@RequestMapping("/product")
public class PmsProductController {
@Autowired
private PmsProductService productService;
@ApiOperation("Create an 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();
}
}
}
Sonrasında veritabanından kullanıcının izin değerini sorgulayın ve bunu UserDetails nesnesine ayarlayın. Bu yöntemin uygulanması kolay olsa da izin değerini doğrudan yöntem üzerine yazmak pek zarif bir yaklaşım değildir.
/**
* UmsAdminService implementation class
* Created by macro on 2018/4/26.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public UserDetails loadUserByUsername(String username){
//get user information
UmsAdmin admin = getAdminByUsername(username);
if (admin != null) {
List permissionList = getPermissionList(admin.getId());
return new AdminUserDetails(admin,permissionList);
}
throw new UsernameNotFoundException("Incorrect username or password");
}
}
Aslında her arayüze karşılık gelen yol benzersizdir ve arayüzün izinlerini yol üzerinden kontrol etmek daha zarif bir çözümdür.
İlk olarak dinamik izinler için bir filtre oluşturmamız gerekir. Buradaki doFilter yöntemine dikkat edin; bu yöntem OPTIONS isteklerini ve beyaz liste isteklerini serbest bırakmak için kullanılır. Kimlik doğrulama işlemi için AccessDecisionManager içindeki decide yöntemini çağıracak olan super.beforeInvocation(fi) yöntemini çağırır;
/**
* Dynamic permission filter for path-based dynamic permission filtering
* Created by macro on 2020/2/7.
*/
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);
//OPTIONS request is released directly
if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
//The whitelist request is released directly
PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) {
if(pathMatcher.match(path,request.getRequestURI())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
}
//Here will call the decide method in AccessDecisionManager for authentication operation
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;
}
}
Ardından, AccessDecisionManager sınıfından miras alan bir sınıf oluşturmamız gerekir. Karar verme (decide) yöntemini kullanarak erişim arayüzünün gerektirdiği izinleri kullanıcının sahip olduğu izinlerle eşleştirin; eşleşme sağlanırsa istek serbest bırakılır;
/**
* Dynamic permission decision manager, used to determine whether the user has access rights
* Created by macro on 2020/2/7.
*/
public class DynamicAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
// Release directly when the interface is not configured with resources
if (CollUtil.isEmpty(configAttributes)) {
return;
}
Iterator iterator = configAttributes.iterator();
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
// Compare access to required resources or user-owned resources
String needAuthority = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("Sorry, you don't have access rights");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
Yukarıdaki decide yöntemindeki configAttributes özniteliği FilterInvocationSecurityMetadataSource sınıfının getAttributes yönteminden alındığından, bu sınıftan miras alan yeni bir sınıf oluşturmamız gerekir. getAttributes yöntemi, mevcut yola erişmek için gereken izin değerini almak amacıyla kullanılabilir;
/**
* Dynamic permission data source for obtaining dynamic permission rules
* Created by macro on 2020/2/7.
*/
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<>();
//Get the currently accessed path
String url = ((FilterInvocation) o).getRequestUrl();
String path = URLUtil.getPath(url);
PathMatcher pathMatcher = new AntPathMatcher();
Iterator iterator = configAttributeMap.keySet().iterator();
//Get the resources needed to access the path
while (iterator.hasNext()) {
String pattern = iterator.next();
if (pathMatcher.match(pattern, path)) {
configAttributes.add(configAttributeMap.get(pattern));
}
}
// The operation request permission is not set, return an empty collection
return configAttributes;
}
@Override
public Collection getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
Burada belirtilmesi gereken nokta, tüm yollara karşılık gelen izin değeri verilerinin özel DynamicSecurityService sınıfından geldiğidir;
/**
* Dynamic permission related business class
* Created by macro on 2020/2/7.
*/
public interface DynamicSecurityService {
/**
* Load resource ANT wildcard and resource corresponding MAP
*/
Map loadDataSource();
}
Her şey hazır olduğunda, dinamik izin filtresini FilterSecurityInterceptor önüne ekleyin;
/**
* New usage configuration for SpringSecurity 5.4.x and above
* To avoid circular dependencies, only used to configure HttpSecurity
* Created by macro on 2022/5/19.
*/
@Configuration
public class SecurityConfig {
@Autowired
private DynamicSecurityService dynamicSecurityService;
@Autowired
private DynamicSecurityFilter dynamicSecurityFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omit some configuration...
//Add dynamic permission verification filter when there is dynamic permission configuration
if(dynamicSecurityService!=null){
registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
}
return httpSecurity.build();
}
}
Bu makaleyi okuduysanız oturum açma kimlik doğrulamasını gerçekleştirmek için SpringSecurity+JWT entegrasyonunun yalnızca dört adım sürdüğünü görmüşsünüzdür. Öyleyse, biri oturum açmış kullanıcı bilgilerini almaktan, diğeri ise saklanan dinamik izin kurallarını almaktan sorumlu olan bu iki bean'i yapılandırmanız gerektiğini biliyorsunuzdur. Spring Security'nin yeni kullanımına uyum sağlamak için artık SecurityConfig sınıfından miras almıyoruz, bu da süreci çok daha basit hale getiriyor.
/**
* mall-security module related configuration
* Custom configuration to configure how to obtain user information and dynamic permissions
* Created by macro on 2022/5/20.
*/
@Configuration
public class MallSecurityConfig {
@Autowired
private UmsAdminService adminService;
@Bean
public UserDetailsService userDetailsService() {
//Get the login user information
return username -> {
AdminUserDetails admin = adminService.getAdminByUsername(username);
if (admin != null) {
return admin;
}
throw new UsernameNotFoundException("Incorrect username or password");
};
}
@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;
}
};
}
}
Etki testi
Şimdi örnek projemiz olan mall-tiny-security'yi başlatalım. Aşağıdaki hesap ve şifre ile oturum açın; bu hesap yalnızca /brand/listAll yoluna erişim için yapılandırılmıştır. Erişim adresi: http://localhost:8088/swagger-ui/
Ardından döndürülen jetonu Swagger'ın kimlik doğrulama başlığına yerleştirin;
Yetkilendirilmiş arayüze eriştiğimizde veriler normal şekilde alınabilir;
İzinsiz bir arayüze erişmeye çalıştığımızda ise sistem bize erişim izni olmadığını belirten arayüz uyarısını döndürür.
Spring Security'nin yükseltilmiş kullanımı gerçekten yeterince zarif, basit ve önceki kullanımlarla daha uyumludur. Kişisel olarak, olgun bir çerçevenin yükseltme sürecinde kullanım şeklini büyük ölçüde değiştirmeyeceğini düşünüyorum. Değişiklik yapılsa bile önceki kullanımla uyumlu olacaktır. Bu nedenle çoğu çerçevede eski sürüm nasıl kullanıldıysa yeni sürüm de aynı şekilde kullanılmaya devam edilebilir.
SpringBoot gerçek dünya e-ticaret projesi mall (50 bin+ yıldız) adresi: github.com/macrozheng/…
Temel kullanım
Öncelikle Spring Security tarafından sağlanan temel işlev olan oturum açma kimlik doğrulamasını karşılaştırarak yeni sürümün daha iyi olup olmadığını görelim.
Yükseltilmiş sürüm
İlk olarak projenin pom.xml dosyasını değiştirin ve Spring Boot sürümünü 2.7.0'a yükseltin.
Eski kullanım
Spring Boot 2.7.0 öncesindeki sürümlerde, WebSecurityConfigurerAdapter sınıfından miras alan bir yapılandırma sınıfı yazmamız ve ardından yapılandırma için Adapter içindeki üç yöntemi geçersiz kılmamız gerekiyordu;
/**
* SpringSecurity configuration
* Created by macro on 2018/4/26.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UmsAdminService adminService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//Omit the configuration of HttpSecurity
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Bu kodu SpringBoot 2.7.0 sürümünde kullanırsanız WebSecurityConfigurerAdapter sınıfının kullanımdan kaldırıldığını görürsünüz; görünüşe göre Spring Security bu kullanımı kesin olarak terk ediyor.
Yeni kullanım
Yeni kullanım oldukça basittir; WebSecurityConfigurerAdapter sınıfından miras almanıza gerek yoktur. Doğrudan yapılandırma sınıfını bildirin, SecurityFilterChainBean oluşturmak için bir yöntem yapılandırın ve orijinal HttpSecurity yapılandırmasını bu yönteme taşıyın.
/**
* New usage configuration for SpringSecurity 5.4.x and above
* To avoid circular dependencies, only used to configure HttpSecurity
* Created by macro on 2022/5/19.
*/
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omit the configuration of HttpSecurity
return httpSecurity.build();
}
}
Yeni kullanım oldukça basit ve anlaşılırdır; WebSecurityConfigurerAdapter sınıfından miras alma ve yöntemi geçersiz kılma işlemini ortadan kaldırır. Bu güncellemeyi yapmanız şiddetle tavsiye edilir.
Gelişmiş kullanım
Spring Boot 2.7.0 sürümüne yükseltme yaptıktan sonra Spring Security, yapılandırma yönteminde büyük değişiklikler yaptı. Peki bu durum diğer kullanımları etkiliyor mu? Aslında hiçbir etkisi yok. Şimdi dinamik izin kontrolü uygulamak için Spring Security'nin nasıl kullanılacağını anlatalım.
Yöntem tabanlı dinamik izinler
Öncelikle yöntem tabanlı dinamik izin kontrolünden bahsedelim. Bu yöntemin uygulanması basit olsa da bazı dezavantajları vardır.
Etkinleştirmek için yapılandırma sınıfında @EnableGlobalMethodSecurity notunu kullanın;
/**
* SpringSecurity configuration
* Created by macro on 2018/4/26.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
}
Ardından arayüze erişmek için gereken izinleri yapılandırmak üzere yöntemde @PreAuthorize notunu kullanın;
/**
* Commodity Management Controller
* Created by macro on 2018/4/26.
*/
@Controller
@Api(tags = "PmsProductController", description = "Product Management")
@RequestMapping("/product")
public class PmsProductController {
@Autowired
private PmsProductService productService;
@ApiOperation("Create an 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();
}
}
}
Sonrasında veritabanından kullanıcının izin değerini sorgulayın ve bunu UserDetails nesnesine ayarlayın. Bu yöntemin uygulanması kolay olsa da izin değerini doğrudan yöntem üzerine yazmak pek zarif bir yaklaşım değildir.
/**
* UmsAdminService implementation class
* Created by macro on 2018/4/26.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public UserDetails loadUserByUsername(String username){
//get user information
UmsAdmin admin = getAdminByUsername(username);
if (admin != null) {
List
return new AdminUserDetails(admin,permissionList);
}
throw new UsernameNotFoundException("Incorrect username or password");
}
}
Yol tabanlı dinamik izinler
Aslında her arayüze karşılık gelen yol benzersizdir ve arayüzün izinlerini yol üzerinden kontrol etmek daha zarif bir çözümdür.
İlk olarak dinamik izinler için bir filtre oluşturmamız gerekir. Buradaki doFilter yöntemine dikkat edin; bu yöntem OPTIONS isteklerini ve beyaz liste isteklerini serbest bırakmak için kullanılır. Kimlik doğrulama işlemi için AccessDecisionManager içindeki decide yöntemini çağıracak olan super.beforeInvocation(fi) yöntemini çağırır;
/**
* Dynamic permission filter for path-based dynamic permission filtering
* Created by macro on 2020/2/7.
*/
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);
//OPTIONS request is released directly
if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
//The whitelist request is released directly
PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) {
if(pathMatcher.match(path,request.getRequestURI())){
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return;
}
}
//Here will call the decide method in AccessDecisionManager for authentication operation
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;
}
}
Ardından, AccessDecisionManager sınıfından miras alan bir sınıf oluşturmamız gerekir. Karar verme (decide) yöntemini kullanarak erişim arayüzünün gerektirdiği izinleri kullanıcının sahip olduğu izinlerle eşleştirin; eşleşme sağlanırsa istek serbest bırakılır;
/**
* Dynamic permission decision manager, used to determine whether the user has access rights
* Created by macro on 2020/2/7.
*/
public class DynamicAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection
// Release directly when the interface is not configured with resources
if (CollUtil.isEmpty(configAttributes)) {
return;
}
Iterator
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
// Compare access to required resources or user-owned resources
String needAuthority = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("Sorry, you don't have access rights");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
Yukarıdaki decide yöntemindeki configAttributes özniteliği FilterInvocationSecurityMetadataSource sınıfının getAttributes yönteminden alındığından, bu sınıftan miras alan yeni bir sınıf oluşturmamız gerekir. getAttributes yöntemi, mevcut yola erişmek için gereken izin değerini almak amacıyla kullanılabilir;
/**
* Dynamic permission data source for obtaining dynamic permission rules
* Created by macro on 2020/2/7.
*/
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
//Get the currently accessed path
String url = ((FilterInvocation) o).getRequestUrl();
String path = URLUtil.getPath(url);
PathMatcher pathMatcher = new AntPathMatcher();
Iterator
//Get the resources needed to access the path
while (iterator.hasNext()) {
String pattern = iterator.next();
if (pathMatcher.match(pattern, path)) {
configAttributes.add(configAttributeMap.get(pattern));
}
}
// The operation request permission is not set, return an empty collection
return configAttributes;
}
@Override
public Collection
return null;
}
@Override
public boolean supports(Class aClass) {
return true;
}
}
Burada belirtilmesi gereken nokta, tüm yollara karşılık gelen izin değeri verilerinin özel DynamicSecurityService sınıfından geldiğidir;
/**
* Dynamic permission related business class
* Created by macro on 2020/2/7.
*/
public interface DynamicSecurityService {
/**
* Load resource ANT wildcard and resource corresponding MAP
*/
Map
}
Her şey hazır olduğunda, dinamik izin filtresini FilterSecurityInterceptor önüne ekleyin;
/**
* New usage configuration for SpringSecurity 5.4.x and above
* To avoid circular dependencies, only used to configure HttpSecurity
* Created by macro on 2022/5/19.
*/
@Configuration
public class SecurityConfig {
@Autowired
private DynamicSecurityService dynamicSecurityService;
@Autowired
private DynamicSecurityFilter dynamicSecurityFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
//Omit some configuration...
//Add dynamic permission verification filter when there is dynamic permission configuration
if(dynamicSecurityService!=null){
registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
}
return httpSecurity.build();
}
}
Bu makaleyi okuduysanız oturum açma kimlik doğrulamasını gerçekleştirmek için SpringSecurity+JWT entegrasyonunun yalnızca dört adım sürdüğünü görmüşsünüzdür. Öyleyse, biri oturum açmış kullanıcı bilgilerini almaktan, diğeri ise saklanan dinamik izin kurallarını almaktan sorumlu olan bu iki bean'i yapılandırmanız gerektiğini biliyorsunuzdur. Spring Security'nin yeni kullanımına uyum sağlamak için artık SecurityConfig sınıfından miras almıyoruz, bu da süreci çok daha basit hale getiriyor.
/**
* mall-security module related configuration
* Custom configuration to configure how to obtain user information and dynamic permissions
* Created by macro on 2022/5/20.
*/
@Configuration
public class MallSecurityConfig {
@Autowired
private UmsAdminService adminService;
@Bean
public UserDetailsService userDetailsService() {
//Get the login user information
return username -> {
AdminUserDetails admin = adminService.getAdminByUsername(username);
if (admin != null) {
return admin;
}
throw new UsernameNotFoundException("Incorrect username or password");
};
}
@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;
}
};
}
}
Etki testi
Şimdi örnek projemiz olan mall-tiny-security'yi başlatalım. Aşağıdaki hesap ve şifre ile oturum açın; bu hesap yalnızca /brand/listAll yoluna erişim için yapılandırılmıştır. Erişim adresi: http://localhost:8088/swagger-ui/
Ardından döndürülen jetonu Swagger'ın kimlik doğrulama başlığına yerleştirin;
Yetkilendirilmiş arayüze eriştiğimizde veriler normal şekilde alınabilir;
İzinsiz bir arayüze erişmeye çalıştığımızda ise sistem bize erişim izni olmadığını belirten arayüz uyarısını döndürür.
Özet
Spring Security'nin yükseltilmiş kullanımı gerçekten yeterince zarif, basit ve önceki kullanımlarla daha uyumludur. Kişisel olarak, olgun bir çerçevenin yükseltme sürecinde kullanım şeklini büyük ölçüde değiştirmeyeceğini düşünüyorum. Değişiklik yapılsa bile önceki kullanımla uyumlu olacaktır. Bu nedenle çoğu çerçevede eski sürüm nasıl kullanıldıysa yeni sürüm de aynı şekilde kullanılmaya devam edilebilir.
İlgili Makaleler
-
Veri Depolama İçin 6 İsteğe Bağlı Teknoloji
Bilgi Bankası Ekibi
Daha Fazla Özel Teklifi Keşfedin
-
Kısa Mesaj Servisi (SMS) & E-posta Hizmeti
50.000 e-posta paketi 1,99 USD'den, 120 kısa mesaj ise yalnızca 1,00 USD'den başlayan fiyatlarla.
