zoukankan      html  css  js  c++  java
  • 基于SpringSecurity 开发手机验证码登录

    html

     

     实现短信认证流程

    实现短信验证码接口
    1. 生成验证码
    2. 存入session
    3. 发送给手机验证码

    仿写短信认证逻辑

    	1. token认证信息,创建token
    	SmsCodeAuthenticationToken extends AbstractAuthenticationToken
    	2. 创建	 Provider, 根据token类型AuthenticationManager 会调用相应的Provider,获取UserDetailsService user信息等
    	SmsCodeAuthenticationProvider implements AuthenticationProvider
    	3. 创建filter 具体拦截什么请求,生成 Token信息
    	SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter
    	4. 创建配置类
    	SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> 
    	实现configure 方法 添加自定义的SmsCodeAuthenticationProvider, 和SmsCodeAuthenticationFilter
    	5.在项目中引用:
    	.apply(smsCodeAuthenticationSecurityConfig);
    

      1.SmsCodeAuthenticationToken.java

    package com.imooc.security.core.authentication.mobile;
    
    import org.springframework.security.authentication.AbstractAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.SpringSecurityCoreVersion;
    
    import java.util.Collection;
    
    /**
     * @Title: SmsCodeAuthenticationToken
     * @ProjectName spring-security-main
     * @date 2020/12/211:17
     */
    public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    
    
        private final Object principal; // 登录之前存手机号, 登录之后存用户信息
        public SmsCodeAuthenticationToken(String mobile) {
            super(null);
            this.principal = mobile;
            setAuthenticated(false);
        }
    
        public SmsCodeAuthenticationToken(Object principal,
                                                   Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            super.setAuthenticated(true); // must use super, as we override
        }
    
        // ~ Methods
        // ========================================================================================================
    
        public Object getCredentials() {
            return null;
        }
    
        public Object getPrincipal() {
            return this.principal;
        }
    
        public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
            if (isAuthenticated) {
                throw new IllegalArgumentException(
                        "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
            }
    
            super.setAuthenticated(false);
        }
    
        @Override
        public void eraseCredentials() {
            super.eraseCredentials();
        }
    }
    

      

    2. 创建	 Provider
    package com.imooc.security.core.authentication.mobile;
    
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.InternalAuthenticationServiceException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    
    /**
     * @Title: SmsCodeAuthenticationProvider
     * @ProjectName spring-security-main
     * @date 2020/12/211:37
     */
    public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
        private UserDetailsService userDetailsService;
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            // 1 获取token
            SmsCodeAuthenticationToken smsCodeAuthenticationToken = (SmsCodeAuthenticationToken) authentication;
            // 2. 获取user
            UserDetails user = userDetailsService.loadUserByUsername((String) smsCodeAuthenticationToken.getPrincipal());
            if (user == null) {
                throw new InternalAuthenticationServiceException("无法获取用户信息");
            }
            SmsCodeAuthenticationToken token = new SmsCodeAuthenticationToken(user, user.getAuthorities());
            token.setDetails(smsCodeAuthenticationToken.getDetails());
            return token;
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
        public UserDetailsService getUserDetailsService() {
            return userDetailsService;
        }
    
        public void setUserDetailsService(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    }
    

     

    3. 创建filter 具体拦截什么请求
    package com.imooc.security.core.authentication.mobile;
    
    import org.springframework.security.authentication.AuthenticationServiceException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
    import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @Title: SmsCodeAuthenticationFilter
     * @ProjectName spring-security-main
     * @date 2020/12/211:24
     */
    public class SmsCodeAuthenticationFilter extends
            AbstractAuthenticationProcessingFilter {
        // ~ Static fields/initializers
        // =====================================================================================
    
        public static final String IMOOC_FORM_MOBILE_KEY = "mobile";
    
        private String mobileParameter = IMOOC_FORM_MOBILE_KEY;
        private boolean postOnly = true;
    
        // ~ Constructors
        // ===================================================================================================
    
        public SmsCodeAuthenticationFilter() {
            super(new AntPathRequestMatcher("/authentication/mobile", "POST"));
        }
    
        // ~ Methods
        // ========================================================================================================
    
        public Authentication attemptAuthentication(HttpServletRequest request,
                                                    HttpServletResponse response) throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }
    
            String mobile = obtainMobile(request);
    
    
            if (mobile == null) {
                mobile = "";
            }
    
            mobile = mobile.trim();
    
            SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
    
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
    
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    
        protected String obtainMobile(HttpServletRequest request) {
            return request.getParameter(mobileParameter);
        }
    
        protected void setDetails(HttpServletRequest request,
                                  SmsCodeAuthenticationToken authRequest) {
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        }
    
        public void setPostOnly(boolean postOnly) {
            this.postOnly = postOnly;
        }
    }
    

      

    4. 创建配置类
    package com.imooc.security.core.authentication.mobile;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.web.DefaultSecurityFilterChain;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.stereotype.Component;
    @Component
    public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    
    	// 自定义的成功失败处理器
    	@Autowired
    	private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
    	@Autowired
    	private AuthenticationFailureHandler imoocAuthenticationFailureHandler;
    	
    	@Autowired
    	private UserDetailsService userDetailsService;
    
    	/**
    	 * 加入自定义的filter , provider
    	 * @param http
    	 * @throws Exception
    	 */
    	@Override
    	public void configure(HttpSecurity http) throws Exception {
    		
    		SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
    		smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    		smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(imoocAuthenticationSuccessHandler);
    		smsCodeAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
    		
    		SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
    		smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
    		
    		http.authenticationProvider(smsCodeAuthenticationProvider)
    			.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    		
    	}
    
    }
    

      

    5.在项目中引用:
    package com.imooc.security.browser;
    
    import com.imooc.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
    import com.imooc.security.core.properties.SecurityProperties;
    import com.imooc.security.core.validate.code.SmsCodeFilter;
    import com.imooc.security.core.validate.code.ValidateCodeFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import javax.sql.DataSource;
    
    @Configuration
    public class BrowserSecurityConfig  extends WebSecurityConfigurerAdapter {
        @Autowired
        private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
        @Autowired
        private AuthenticationFailureHandler imoocAuthenctiationFailureHandler;
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new SCryptPasswordEncoder();
        }
    
        @Autowired
        private SecurityProperties securityProperties;
    //    PersistentTokenRepository
    
        @Autowired
        private DataSource dataSource;
    
        @Autowired
        private UserDetailsService userDetailsService; // MyUserDetailsService
        /**
         * 记住我功能
         * 1. 创建PersistentTokenRepository
         * 2. 设置过期时间
         * 3. 获取UserDetailsService 用户登录信息
         * 4. 配置rememberMe 生效
         * @return
         */
        @Bean
        public PersistentTokenRepository persistentTokenRepository() {
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            jdbcTokenRepository.setDataSource(dataSource); // 配置的dataSource
    //        jdbcTokenRepository.setCreateTableOnStartup(true); // 自动创建存放记住我的表,如果存在会报错
            return jdbcTokenRepository;
        }
    
        @Autowired
        private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
        @Override
        public void configure(HttpSecurity http) throws Exception {
            // 图片验证码过滤器
            ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
            validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenctiationFailureHandler);
            validateCodeFilter.setSecurityProperties(securityProperties);
            validateCodeFilter.afterPropertiesSet();
    
            // 验证码过滤器
            SmsCodeFilter smsCodeFilter = new SmsCodeFilter();
            smsCodeFilter.setAuthenticationFailureHandler(imoocAuthenctiationFailureHandler);
            smsCodeFilter.setSecurityProperties(securityProperties);
            smsCodeFilter.afterPropertiesSet();
    
            // 添加一个图片验证filter, 在UsernamePasswordAuthenticationFilter之前执行
            http
                    .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
                    .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                    // .httpBasic() // 默认方式
                    .formLogin() // 设置认证的登录方式 表单方式
                    .loginPage("/authentication/require") // 自定义登录页面
                    .loginProcessingUrl("/authentication/form") // 自定义表单提交的url, 默认是login
                    .successHandler(imoocAuthenticationSuccessHandler) // 不适用默认的认证成功处理器
                    .failureHandler(imoocAuthenctiationFailureHandler) // 登录失败处理器
    //                .failureForwardUrl("/authentication/require")
    //                .failureUrl("/authentication/require")
                    .and()
                    .rememberMe()
                    .tokenRepository(persistentTokenRepository())
                    // rememberME 有效期
                    .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                    .userDetailsService(userDetailsService)
                    .and()
                    .authorizeRequests() // 需要授权
                    // 当匹配到这个页面时,不需要授权
                    .antMatchers("/authentication/require", securityProperties.getBrowser().getLoginPage(),
                            "/code/*").permitAll()
                    .anyRequest() // 所有请求
                    .authenticated()
                    .and()  // 关闭csrf
                    .csrf()
                    .disable()
                    .apply(smsCodeAuthenticationSecurityConfig);
        }
    }
    

      

    controller 

    	@GetMapping("/code/sms")
    	public void createSmsCode(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletRequestBindingException {
    		ValidateCode smsCode = smsCodeGenerator.generate(new ServletWebRequest(request));
    		// 放入session
    		sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_SMS_KEY, smsCode);
    		// 通过短信服务商发送短信验证码到手机
    		String mobile = ServletRequestUtils.getStringParameter(request, "mobile");
    		smsCodeSender.send(mobile, smsCode.getCode());
    	}
    

      

     

  • 相关阅读:
    20171017/20171018
    BZOJ[3193] [JLOI2013]地形生成
    BZOJ[1009] [HNOI2008]GT考试
    BZOJ[4767] 两双手
    BZOJ[4013] [HNOI2015]实验比较
    BZOJ[1925] [Sdoi2010]地精部落
    20171015 杂题
    20171015
    20171014
    USACO 2015 December Contest, Gold Problem 3. Bessie's Dream
  • 原文地址:https://www.cnblogs.com/412013cl/p/14095782.html
Copyright © 2011-2022 走看看