English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Spring Security使用分类:
如何使用Spring Security,相信百度过的人都知道,总共有四种用法,从简单到复杂为:
1、不使用数据库,所有数据都写在配置文件中,这也是官方文档中的demo;
2、使用数据库,根据Spring Security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不够灵活,而且那个数据库设计得很简陋,实用性差;
3、Spring Security与Acegi不同,它不能修改默认filter了,但支持插入filter,因此我们可以插入自己的filter来灵活使用;
4.استخدام الطرق القوية، تعديل الشيفرة المصدرية، ما ذكرناه من تعديل filter الافتراضي هو مجرد تعديل ملف التكوين لت替代 filter، هذا التعديل يغير الشيفرة الداخلية مباشرة، ولكن هذا غير متوافق مع مبادئ تصميم الـ OO، وهو غير عملي وغير مستخدم.
هذا المقال يقدم محتوى حول تحقق الدخول المخصص لـ spring security، ويساهم في مشاركة المعرفة للاستفادة من الجميع، لن نتحدث كثيرًا، دعونا نرى الشرح التفصيلي الآن.
1.ملخص
1.1.مقدمة
spring security هي إطار أمان يعتمد على Spring AOP وServlet Filters لإدارة التحقق والصلاحيات.
1.2.مسار التحقق المخصص لـ spring security
1) عملية التحقق
إنشاء Token التحقق غير الناجح
↑(الحصول على معلومات) (توزيع provider بناءً على Token التحقق) AuthenticationFilter -> AuthenticationManager -> AuthenticationProvider ↓(التحقق) UserDetails (عادة يتم الحصول عليها من قاعدة البيانات) ↓(من خلال) إنشاء Token التحقق الناجح ↓(يخزن) SecurityContextHolder
2) أضف AuthenticationFilter إلى سلسلة التصفية الخاصة بالأمان (في ملف تكوين الخادم المصدر)، مثل:
http.addFilterBefore(AuthenticationFilter, AbstractPreAuthenticatedProcessingFilter.class)
أو:
http.addFilterAfter(AuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
2.بمثابة مثال على الدخول عبر رسائل SMS باستخدام الرقم الهاتف
2.1.بيئة التطوير
2.2.تحليل الشيفرة الأساسية
2.2.1.مسار التحقق المخصص للدخول
2.2.1.1.Token التحقق المخصص للدخول
/** * Token للدخول عبر الهاتف * * @author : CatalpaFlat */ public class MobileLoginAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationToken.class.getName()); private final Object principal; public MobileLoginAuthenticationToken(String mobile) { super(null); this.principal = mobile; this.setAuthenticated(false); logger.info("MobileLoginAuthenticationToken setAuthenticated ->false loading ..."); } public MobileLoginAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; // يجب استخدام super، لأننا نعيد تعريف super.setAuthenticated(true); logger.info("MobileLoginAuthenticationToken setAuthenticated ->true loading ..."); } @Override public void setAuthenticated(boolean authenticated) { if (authenticated) { throw new IllegalArgumentException; "لا يمكن تعيين هذا التoken كموثوق - استخدم بناءً على المعين الذي يأخذ قائمة GrantedAuthority بدلاً من ذلك"; } super.setAuthenticated(false); } @Override public Object getCredentials() { return null; } @Override public Object getPrincipal() { return this.principal; } @Override public void eraseCredentials() { super.eraseCredentials(); } }
ملاحظة:
setAuthenticated():判断是否已认证
2.2.1.1.自定义认证登录过滤器
/** * 手机短信登录过滤器 * * @author : CatalpaFlat */ public class MobileLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter { private boolean postOnly = true; private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationFilter.class.getName()); @Getter @Setter private String mobileParameterName; public MobileLoginAuthenticationFilter(String mobileLoginUrl, String mobileParameterName, String httpMethod) { super(new AntPathRequestMatcher(mobileLoginUrl, httpMethod)); this.mobileParameterName = mobileParameterName; logger.info("MobileLoginAuthenticationFilter التحميل... "); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) { throw new AuthenticationServiceException("طريقة التحقق غير مدعومة: " + request.getMethod()); } // الحصول على رقم الهاتف String mobile = obtainMobile(request); // تجميع التoken MobileLoginAuthenticationToken authRequest = new MobileLoginAuthenticationToken(mobile); //سمح للفرز الفرعي بإعداد الخاصية "details" setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * إعداد تفاصيل التحقق من الهوية */ private void setDetails(HttpServletRequest request, MobileLoginAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } /** * الحصول على رقم الهاتف */ private String obtainMobile(HttpServletRequest request) { return request.getParameter(mobileParameterName); } public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } }
ملاحظة:attemptAuthentication()方法:
2.2.1.1.自定义认证登录提供者
/** * 手机短信登录认证提供者 * * @author : CatalpaFlat */ public class MobileLoginAuthenticationProvider implements AuthenticationProvider { private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationProvider.class.getName()); @Getter @Setter private UserDetailsService customUserDetailsService; public MobileLoginAuthenticationProvider() { logger.info("MobileLoginAuthenticationProvider loading ..."); } /** * 认证 */ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { //获取过滤器封装的token信息 MobileLoginAuthenticationToken authenticationToken = (MobileLoginAuthenticationToken) authentication; //获取用户信息(数据库认证) UserDetails userDetails = customUserDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); //不通过 if (userDetails == null) { throw new InternalAuthenticationServiceException("Unable to obtain user information"); } //通过 MobileLoginAuthenticationToken authenticationResult = new MobileLoginAuthenticationToken(userDetails, userDetails.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } /** * 根据token类型,来判断使用哪个Provider */ @Override public boolean supports(Class<?> authentication) { return MobileLoginAuthenticationToken.class.isAssignableFrom(authentication); } }
ملاحظة:authenticate()方法
2.2.1.1.自定义认证登录认证配置
@Configuration(SpringBeanNameConstant.DEFAULT_CUSTOM_MOBILE_LOGIN_AUTHENTICATION_SECURITY_CONFIG_BN) public class MobileLoginAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationSecurityConfig.class.getName()); @Value("${login.mobile.url}") private String defaultMobileLoginUrl; @Value("${login.mobile.parameter}") private String defaultMobileLoginParameter; @Value("${login.mobile.httpMethod}") private String defaultMobileLoginHttpMethod; @Autowired private CustomYmlConfig customYmlConfig; @Autowired private UserDetailsService customUserDetailsService; @Autowired private AuthenticationSuccessHandler customAuthenticationSuccessHandler; @Autowired private AuthenticationFailureHandler customAuthenticationFailureHandler; public MobileLoginAuthenticationSecurityConfig() { logger.info("MobileLoginAuthenticationSecurityConfig loading ..."); } @Override public void configure(HttpSecurity http) throws Exception { MobilePOJO mobile = customYmlConfig.getLogins().getMobile(); String url = mobile.getUrl(); String parameter = mobile.getParameter().getMobile(); String httpMethod = mobile.getHttpMethod(); MobileLoginAuthenticationFilter mobileLoginAuthenticationFilter = new MobileLoginAuthenticationFilter(StringUtils.isBlank(url) ? defaultMobileLoginUrl : url, StringUtils.isBlank(parameter) ? defaultMobileLoginUrl : parameter, StringUtils.isBlank(httpMethod) ? defaultMobileLoginHttpMethod : httpMethod); mobileLoginAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); mobileLoginAuthenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler); mobileLoginAuthenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler); MobileLoginAuthenticationProvider mobileLoginAuthenticationProvider = new MobileLoginAuthenticationProvider(); mobileLoginAuthenticationProvider.setCustomUserDetailsService(customUserDetailsService); http.authenticationProvider(mobileLoginAuthenticationProvider) .addFilterAfter(mobileLoginAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } }
ملاحظة:configure()方法
实例化AuthenticationFilter和AuthenticationProvider
将AuthenticationFilter和AuthenticationProvider添加到spring security中。
2.2.2.基于redis的自定义验证码校验
2.2.2.1.基于redis的自定义验证码过滤器
/** * 验证码过滤器 * * @author : CatalpaFlat */ @Component(SpringBeanNameConstant.DEFAULT_VALIDATE_CODE_FILTER_BN) public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(ValidateCodeFilter.class.getName()); @Autowired private CustomYmlConfig customYmlConfig; @Autowired private RedisTemplate<Object, Object> redisTemplate; /** * 工具类,用于验证请求url与配置的url是否匹配 */ private AntPathMatcher pathMatcher = new AntPathMatcher(); public ValidateCodeFilter() { logger.info("Loading ValidateCodeFilter..."); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String url = customYmlConfig.getLogins().getMobile().getUrl(); if (pathMatcher.match(url, request.getRequestURI())) { String deviceId = request.getHeader("deviceId"); if (StringUtils.isBlank(deviceId)) { throw new CustomException(HttpStatus.NOT_ACCEPTABLE.value(), "Not deviceId in the head of the request"); } String codeParamName = customYmlConfig.getLogins().getMobile().getParameter().getCode(); String code = request.getParameter(codeParamName); if (StringUtils.isBlank(code)) { throw new CustomException(HttpStatus.NOT_ACCEPTABLE.value(), "Not code in the parameters of the request"); } String key = SystemConstant.DEFAULT_MOBILE_KEY_PIX + deviceId; SmsCodePO smsCodePo = (SmsCodePO) redisTemplate.opsForValue().get(key); if (smsCodePo.isExpried()){ throw new CustomException(HttpStatus.BAD_REQUEST.value(), "The verification code has expired"); } String smsCode = smsCodePo.getCode(); if (StringUtils.isBlank(smsCode)) { throw new CustomException(HttpStatus.BAD_REQUEST.value(), "Verification code does not exist"); } if (StringUtils.equals(code, smsCode)) { redisTemplate.delete(key); //let it go filterChain.doFilter(request, response); } else { throw new CustomException(HttpStatus.BAD_REQUEST.value(), "رمز التحقق غير صحيح"); } } else { //let it go filterChain.doFilter(request, response); } } }
ملاحظة:doFilterInternal()
تحقق مسبق التحقق من رمز التحقق المخصص
2.2.2.2. إضافة مسبق التحقق من رمز التحقق المخصص إلى سلسلة مرشحات spring security
http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class)
ملاحظة:إضافة إلى مسبق معالجة التحقق
3. تأثير الاختبار
في النهاية، سنقدم عنوان عنوان المصدر:https://gitee.com/CatalpaFlat/springSecurity.git (تنزيل محلي)
النهاية
هذا هو نهاية محتوى هذا المقال، نأمل أن يكون محتوى هذا المقال له قيمة مرجعية لتعلمكم أو عملكم، إذا كان لديكم أي أسئلة، يمكنكم ترك تعليقات للتفاعل، شكرًا لدعمكم لتعليمات الصراخ.
بيان: محتوى هذا المقال تم جمعه من الإنترنت، ملكية المحتوى لصاحب الحقوق، تم جمع المحتوى من قبل المستخدمين على الإنترنت بشكل تلقائي، هذا الموقع لا يمتلك حقوق الملكية، لم يتم تعديل المحتوى بشكل يدوي، ولا يتحمل أي مسؤولية قانونية متعلقة بذلك. إذا كنت قد وجدت محتوى يشتبه في انتهاك حقوق النسخ، فنرجو منك إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال # بـ @) للإبلاغ، وقدم الدليل على الدليل، إذا تم التحقق من ذلك، سيتم حذف المحتوى المزعوم فورًا.