springcloud oauth2 认证服务器自定义手机短信验证码登录(新增Oauth2的grant_type模式,更多模式以此类推)

感谢:https://blog.csdn.net/qq_33396608/article/details/103271320
自己记录一遍操作步骤,防止原作者文章删除找不到。

事前工作:sys_oauth_client_details表authorized_grant_types字段新增:phone_sms
springcloud oauth2 认证服务器自定义手机短信验证码登录(新增Oauth2的grant_type模式,更多模式以此类推)_第1张图片
工具类,获取当前用户:


import com.iwmake.common.core.constant.SystemConstant;
import com.iwmake.common.security.domain.LoginUser;
import com.iwmake.system.api.domain.SysUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.ObjectUtils;

/**
 * 权限获取工具类
 * @author Dylan
 */
public class SecurityUtils {
     
    /**
     * 获取Authentication
     */
    public static Authentication getAuthentication() {
     
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 获取用户
     */
    public static String getUsername() {
     
        return getLoginUser().getUsername();
    }

    /**
     * 获取用户
     */
    public static LoginUser getLoginUser(Authentication authentication) {
     
        Object principal = authentication.getPrincipal();
        if (principal instanceof LoginUser) {
     
            return (LoginUser) principal;
        }
        return null;
    }

    /**
     * 获取用户
     */
    public static LoginUser getLoginUser() {
     
        Authentication authentication = getAuthentication();
        if (authentication == null) {
     
            return null;
        }
        return getLoginUser(authentication);
    }

    /**
     * 生成BCryptPasswordEncoder密码
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password) {
     
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }

    /**
     * 判断密码是否相同
     * @param rawPassword     真实密码
     * @param encodedPassword 加密后字符
     * @return 结果
     */
    public static boolean matchesPassword(String rawPassword, String encodedPassword) {
     
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
}

少说话,多撸代码:
一、java实体,新增userId方便获取(principal时候方便有Id存在):

@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
@ToString
public class LoginUser extends User {
     
    private static final long serialVersionUID = 1L;

    /**
     * 新增用户ID
     */
    private Long userId;
    }

二、继承抽象类AbstractTokenGranter


import java.util.Collection;
import java.util.Map;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import com.iwmake.common.security.domain.LoginUser;

/**
 * 
 *  说明:
*     自定义token授予抽象实现
*     继承抽象类AbstractTokenGranter,我们也是抽象的,后面好扩展 
 *  @author:heshengjin qq:2356899074
 *  @date 2021年2月5日 上午9:51:00
 */
public abstract class CustomAbstractTokenGranter extends AbstractTokenGranter {
     

   public CustomAbstractTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
     
       super(tokenServices, clientDetailsService, requestFactory, grantType);
   }

   @Override
   protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
     
       Map<String, String> parameters = tokenRequest.getRequestParameters();
       LoginUser loginUser = (LoginUser)getUserDetails(parameters);
       if (null == loginUser) {
     
           throw new InvalidGrantException("账户未找到");
       }
       //Object principal, Object credentials,Collection authorities
       Authentication userAuth = new UsernamePasswordAuthenticationToken(loginUser,
    		   loginUser.getPassword(), loginUser.getAuthorities());

       OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
       return new OAuth2Authentication(storedOAuth2Request, userAuth);

   }

   /**
    * 自定义获取用户信息
    */
   protected abstract UserDetails getUserDetails(Map<String, String> parameters);
}


三、定义短信验证码具体逻辑

import java.util.Map;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

/**
 * 
 *  说明:
 *    手机号-短信验证码
 *    定义短信验证码具体逻辑  
 *  @author:heshengjin qq:2356899074
 *  @date 2021年2月5日 上午9:51:34
 */
public class PhoneSmsTokenGranter extends CustomAbstractTokenGranter {
     

    private static final String PHONE_SMS = "phone_sms";

    private CustomUserDetailService customUserDetailService;

    public PhoneSmsTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, CustomUserDetailService  customUserDetailService) {
     
        super(tokenServices, clientDetailsService, requestFactory, PHONE_SMS);
        this.customUserDetailService = customUserDetailService;
    }

    @Override
    protected UserDetails getUserDetails(Map<String, String> parameters) {
     
        String phone = parameters.get("phone");
        String smsCode = parameters.get("sms_code");
        return customUserDetailService.loadByPhone(phone,smsCode);
    }
}


四、验证部分

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.iwmake.common.core.domain.RestResult;
import com.iwmake.common.core.enums.UserStatus;
import com.iwmake.common.core.exception.BaseException;
import com.iwmake.common.core.utils.StringUtils;
import com.iwmake.common.security.domain.LoginUser;
import com.iwmake.system.api.RemoteUserService;
import com.iwmake.system.api.domain.SysUser;
import com.iwmake.system.api.model.UserInfo;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 *  说明:自定义的获取用户信息
 *  @author:heshengjin qq:2356899074
 *  @date 2021年2月5日 上午9:51:52
 */
@Service
@Slf4j
public class CustomUserDetailService {
     
	
	//这里是数据库查询用户操作逻辑代码
    @Autowired
    private RemoteUserService remoteUserService;

	public UserDetails loadByPhone(String phone, String code) {
     
    	//在验证手机号和验证码--load from redis
//        if (!"123456".equals(code)) {
     
//            throw new InvalidGrantException("验证码错误或已过期");
//        }
//        return new User(phone, "", AuthorityUtils.createAuthorityList("user:add", "user:delete"));
		 //查询数据库验证
		 RestResult<UserInfo> userResult = remoteUserService.getUserInfoByPhone(phone);
	     checkUser(userResult, phone);
	     return getUserDetails(userResult);
    }
    //自定义其他登录等
	
	
	
    public void checkUser(RestResult<UserInfo> userResult, String phone) {
     
        if (ObjectUtils.isEmpty(userResult) || ObjectUtils.isEmpty(userResult.getData())) {
     
            log.info("登录手机号:{} 不存在.", phone);
            throw new UsernameNotFoundException("登录手机号:" + phone + " 不存在");
        } else if (UserStatus.DELETED.getCode().equals(userResult.getData().getSysUser().getDelFlag())) {
     
            log.info("登录手机号:{} 已被删除注销.", phone);
            throw new BaseException("对不起,您的手机号:" + phone + " 已被删除注销");
        } else if (UserStatus.DISABLE.getCode().equals(userResult.getData().getSysUser().getStatus())) {
     
            log.info("登录手机号:{} 已被停用.", phone);
            throw new BaseException("对不起,您的手机号:" + phone + " 已停用");
        }
    }
    
    private UserDetails getUserDetails(RestResult<UserInfo> result) {
     
        UserInfo info = result.getData();
        Set<String> dbAuthsSet = new HashSet<String>();
        if (StringUtils.isNotEmpty(info.getRoles())) {
     
            // 获取角色
            dbAuthsSet.addAll(info.getRoles());
            // 获取权限
            dbAuthsSet.addAll(info.getPermissions());
        }

        Collection<? extends GrantedAuthority> authorities = AuthorityUtils
                .createAuthorityList(dbAuthsSet.toArray(new String[0]));
        SysUser user = info.getSysUser();
        System.out.println("==========CustomUserDetailServiceImpl========");
        System.out.println(JSON.toJSONString(user));
        return new LoginUser(user.getUserId(),user.getSuperAdmin(),user.getSuperTenant(), user.getTenantId(),user.getDeptId(),user.getUserName(), user.getPassword(), true, true, true, true,
                authorities);
//        return new User(user.getUserName(), user.getPassword(), true, true, true, true,
//                authorities);
    }

}


五、在你的授权服务器@EnableAuthorizationServerl类加入自定义的token授予模式


    @Autowired
    private CustomUserDetailService customUserDetailService;
 
 	/**
     * 定义授权(authorization)和令牌端点(token)以及令牌服务(token services)
     *
     * @param endpoints 配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
     
        //...
        //在原有的基础上增加自定义手机号短信登录
        List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
        tokenGranters.add(endpoints.getTokenGranter());
        endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));
    }
    
	/**
     * 自定义TokenGranter集合
     */
    private List<TokenGranter> getTokenGranters(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
     
        return new ArrayList<>(Collections.singletonList(
                new PhoneSmsTokenGranter(tokenServices, clientDetailsService, requestFactory, customUserDetailService)
        ));
    }

测试
获取访问的token
springcloud oauth2 认证服务器自定义手机短信验证码登录(新增Oauth2的grant_type模式,更多模式以此类推)_第2张图片
获取当前登录的用户

/**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public Result getInfo() {
     
        //当前登录用户ID
        Long userId = SecurityUtils.getLoginUser().getUserId();
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(userId);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(userId);
        Result ajax = Result.success();
        ajax.put("user", userService.selectUserById(userId));
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }

springcloud oauth2 认证服务器自定义手机短信验证码登录(新增Oauth2的grant_type模式,更多模式以此类推)_第3张图片
springcloud oauth2 认证服务器自定义手机短信验证码登录(新增Oauth2的grant_type模式,更多模式以此类推)_第4张图片

你可能感兴趣的:(springcloud,OAuth2.0,spring,cloud,oauth2)