闲碎记事本 闲碎记事本
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YAN

我要偷偷记录...
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • java

    • SpringBoot

    • SpringSecurity

      • 自定义登录方式
      • MybatisPlus

      • Netty

      • sip

      • 其他

    • linux

    • docker

    • redis

    • nginx

    • mysql

    • 其他

    • 环境搭建

    • 知识库
    • java
    • SpringSecurity
    Yan
    2023-02-21
    目录

    自定义登录方式

    Spring Security,默认实现为账号密码登录

    怎么新增登录方式呢?例如:手机号+ 验证码 ,微信登录

    以下示例:手机号+ 验证码登录

    涉及到的接口

    #用于定义token
    org.springframework.security.authentication.AbstractAuthenticationToken;
    #用于身份验证
    org.springframework.security.authentication.AuthenticationProvider;
    #用于得到授权对象
    org.springframework.security.core.userdetails.UserDetailsService;
    #Security 配置类
    org.springframework.security.core.userdetails.WebSecurityConfigurerAdapter;
    

    # 1:定义Token

    /**
     * 验证码认证Token
     */
    public class CaptchaAuthenticationToken extends AbstractAuthenticationToken {
    
        private final Object phone;
        private String captcha;
    
        /**
         * 此构造函数用来初始化未授信凭据.
         *
         * @param phone   手机号
         * @param captcha 验证码
         */
        public CaptchaAuthenticationToken(Object phone, String captcha) {
            super(null);
            this.phone = phone;
            this.captcha = captcha;
            setAuthenticated(false);
        }
    
        /**
         * 此构造函数用来初始化授信凭据.
         * @param phone       手机号
         * @param captcha 验证码
         * @param authorities  认证
         */
        public CaptchaAuthenticationToken(Object phone, String captcha, Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.phone = phone;
            this.captcha = captcha;
            super.setAuthenticated(true);
        }
    
        public Object getCredentials() {
            return this.captcha;
        }
    
        public Object getPrincipal() {
            return this.phone;
        }
    
    
        @Override
        public void eraseCredentials() {
            super.eraseCredentials();
            captcha = null;
        }
    
        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);
        }
    
    }
    

    # 2:身份认证

    public class CaptchaAuthenticationProvider implements AuthenticationProvider {
    
        private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
    
        private final UserDetailsService userDetailsService;
    
        private final ISmsService smsService;
    
        /**
         * 定义构造器 
         * @param userDetailsService  userDetailsService
         * @param smsService 短信 Service
         */
        public CaptchaAuthenticationProvider(UserDetailsService userDetailsService, ISmsService smsService) {
            this.userDetailsService = userDetailsService;
            this.smsService = smsService;
        }
    
        /**
         * 身份认证
         * @param authentication 需要认证的Token
         * @return 返回认证成功Token / 认证失败则抛出异常
         */
        @Override
        public Authentication authenticate(Authentication authentication) {
    
            CaptchaAuthenticationToken unAuthenticationToken = (CaptchaAuthenticationToken) authentication;
            //得到手机号和 验证码
            String phone = unAuthenticationToken.getName();
            String rawCode = (String) unAuthenticationToken.getCredentials();
    
            //通过自定义实现得到的用户信息
            UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
    
            //用户信息校验 ,业务逻辑是否校验...
    
            Boolean match;
    
            if (match) {
                //验证通过 更新凭证
                return createSuccessAuthentication(authentication, userDetails);
            } else {
                throw new BadCredentialsException("captcha is not matched");
            }
    
        }
    
        /**
         * 认证成功将非授信凭据转为授信凭据.
         * 封装用户信息 角色信息。
         * @return the authentication
         */
        protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
            Collection<? extends GrantedAuthority> authorities = authoritiesMapper.mapAuthorities(user.getAuthorities());
    
            //返回一个 授信凭据
            CaptchaAuthenticationToken authenticationToken = new CaptchaAuthenticationToken(user, null, authorities);
            authenticationToken.setDetails(authentication.getDetails());
            return authenticationToken;
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            //认证器是否是 CaptchaAuthenticationToken
            return CaptchaAuthenticationToken.class.isAssignableFrom(authentication);
        }
    
    
    }
    

    # 3:定义认证对象

    /**
     * 登录用户身份权限
     *
     */
    @Data
    public class LoginUser implements UserDetails {
        private static final long serialVersionUID = 1L;
    
        private String username;
    
        private String password;
    
        private Set<String> permissions;
    
        private Set<String> roles;
    
        //其他用户属性可自行定义
        public LoginUser(String username, Set<String> permissions, Set<String> roles) {
            this.permissions = permissions;
            this.roles = roles;
            this.username = username;
            //因为是手机号验证码登录,密码不重要
            this.password = "";
        }
    
        @JSONField(serialize = false)
        @Override
        public String getPassword() {
            return user.getPassword();
        }
    
        @Override
        public String getUsername() {
            return user.getUserName();
        }
    
        /**
         * 账户是否未过期,过期无法验证
         */
        @JSONField(serialize = false)
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        /**
         * 指定用户是否解锁,锁定的用户无法进行身份验证
         */
        @JSONField(serialize = false)
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        /**
         * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
         */
        @JSONField(serialize = false)
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        /**
         * 是否可用 ,禁用的用户不能身份验证
         */
        @JSONField(serialize = false)
        @Override
        public boolean isEnabled() {
            return true;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return null;
        }
    }
    
    

    # 4:配置认证

    /**
     * 验证码认证配置类
     */
    @Slf4j
    @Configuration
    public class CaptchaAuthenticationConfiguration {
    
    
        /**
         * 实现 UserDetailsService
         * 获取用户信息
         */
        @Bean
        @Qualifier("captchaUserDetailsService")
        public UserDetailsService captchaUserDetailsService() {
            return phone -> {
                SysUser user;
    
                // 获取用户信息 此处省略 ....
    
                //返回一个登录成功的用户  并设置角色,权限
                return new LoginUser(username, permissions, roles);
            };
        }
    
        /**
         * 验证码认证器
         * @param  smsService smsService
         * @param  userDetailsService userDetailsService
         */
        @Bean
        public CaptchaAuthenticationProvider captchaAuthenticationProvider(ISmsService smsService,
                                                                           @Qualifier("captchaUserDetailsService") UserDetailsService userDetailsService
        ) {
            return new CaptchaAuthenticationProvider(userDetailsService, smsService);
        }
    }
    

    添加到配置

    
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
      @Resource
      private  CaptchaAuthenticationProvider captchaAuthenticationProvider;
      
        
        //此处省略其他配置。。。
      
        @Override
        protected void configure(AuthenticationManagerBuilder auth) {
            //添加 CaptchaAuthenticationProvider
            auth.authenticationProvider(captchaAuthenticationProvider);
        }
    }
    
    

    # 5:使用

    
    @Service
    public class LoginService {
        /**
         * 身份验证管理器
         */
        @Resource
        private AuthenticationManager authenticationManager;
    
        public String captchaLogin(String phone,String captcha) {
            String phone = captchaLoginBody.getPhone();
            // 用户验证
            Authentication authentication;
            try {
                //通过验证管理器 去验证 CaptchaAuthenticationToken
                // 该方法会去调用 captchaUserDetailsService.loadUserByUsername
                authentication = authenticationManager.authenticate(new CaptchaAuthenticationToken(phone, captcha));
    
            } catch (Exception e) {
                //认证失败
                throw new ServiceException(e.getMessage());
            }
            //认证成功,得到登录成功用户
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            
            // 下面处理自己的业务逻辑,此处省略....
            return "登录成功";
        }
    
    }
    
    
    上次更新: 2025/11/27, 06:57:56
    JAVA命令行参数以及配置加载顺序
    租户拦截器

    ← JAVA命令行参数以及配置加载顺序 租户拦截器→

    最近更新
    01
    Reactor (Mono & Flux) 速查表
    08-20
    02
    Caddy操作指南
    04-25
    03
    虚拟机磁盘扩展
    04-22
    更多文章>
    Theme by Vdoing | Copyright © 2022-2025 YAN | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式