【Spring Security Oauth2】构建授权服务器(五):自定义(用户登录)认证策略

一、背景介绍

1、通过之前的文章搭建授权服务器时,使用的登录账号和密码是在代码中写入的。而业务都是取数据库中的用户信息进行认证。

【Spring Security Oauth2】构建授权服务器(五):自定义(用户登录)认证策略_第1张图片

二、环境准备

【Spring Security Oauth2】构建授权服务器(一):内存模式

三、自定义(登录)认证策略

1、创建AccountDetailsServiceImpl类继承UserDetailsService类,用于查询数据库中用户信息。文章就简单化了,具体数据库操作需自己接入。

package com.cyun.security.oauth2.config.security.provider;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
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.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 账号服务
 *
 * @author He PanFu
 * @date 2021-12-07 20:36:15
 */
@Service("accountDetailsServiceImpl")
public class AccountDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 获取用户信息
     * @param username 用户名
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    	// 假设是数据库查询出的数据
        List<GrantedAuthority> dba = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_DBA,ROLE_USER,ROLE_ADMIN,SELECT,INSERT,UPDATE,DELETE");
        // 假设这是数据库查询出的数据。参数顺序:用户名、加密后的密码、权限
        return new User("root", passwordEncoder.encode("1"), dba);
    }

}

2、创建AdminPwdAuthenticationProvider类继承AuthenticationProvider类,自定义认证(登录)策略。

package com.cyun.security.oauth2.config.security.provider;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * 账号密码登录策略
 *
 * @author He PanFu
 * @date 2021-12-07 20:36:15
 */
@Component
public class AdminPwdAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    @Qualifier("accountDetailsServiceImpl")
    private UserDetailsService adminUserDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 认证逻辑
     * @param authentication 认证信息
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
        // 查询数据库中用户信息
        UserDetails userDetails = adminUserDetailsService.loadUserByUsername(token.getName());
        // 密码验证
        if(passwordEncoder.matches(authentication.getCredentials().toString(),userDetails.getPassword())){
            return  new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        }
        throw  new BadCredentialsException("用户名密码不正确");
    }

    /**
     * 验证授权token类型
     * @param authentication token类型
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

3、修改configure(AuthenticationManagerBuilder auth)方法

【Spring Security Oauth2】构建授权服务器(五):自定义(用户登录)认证策略_第2张图片

	@Autowired
    private AdminPwdAuthenticationProvider adminPwdAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // String encode = passwordEncoder().encode("1");
        //
        // auth.inMemoryAuthentication()
        //         .withUser("root").password(encode).roles("ADMIN", "DBA");

        //添加Provider
        auth.authenticationProvider(adminPwdAuthenticationProvider);
    }

4、通过以上配置,就能使用数据库中的用户名、密码进行登录了。

四、拓展

1、自定义登录接口,不使用默认的“/oauth/token”接口

方案:
【Spring Security Oauth2】构建授权服务器(五):自定义(用户登录)认证策略_第3张图片

代码:

@Autowired
    private TokenEndpoint tokenEndpoint;


    @GetMapping(value = "/login")
    public String register(@RequestParam Map<String, String> parameters) {
        // client_id 客户端标识、密码(目前未发现其具体作用)、具体权限。此处可添加客户端标识和客户端秘钥认证。
        User clientUser= new User(parameters.get("client_id"),"", new ArrayList<>());

        //生成已经认证的client
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(clientUser,null, new ArrayList<>());
        //调用自带的获取token方法。
        OAuth2AccessToken oAuth2AccessToken = null;
        try {
            oAuth2AccessToken = tokenEndpoint.postAccessToken(token, parameters).getBody();
        } catch (HttpRequestMethodNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(oAuth2AccessToken);
        return oAuth2AccessToken.toString();
    }

注意事项:

  • 调用“/login”接口提示需要登录,需放开拦截控制。
    【Spring Security Oauth2】构建授权服务器(五):自定义(用户登录)认证策略_第4张图片
  • 使用该方式之后,对应的客户端秘钥就会失去价值,代码中不会去验证是否跟客户端标识对应,需手动编写相应逻辑。“/oauth/token”接口是在请求在Controller前就经过了一系列的过滤器链进行客户端标识和密码认证。具体可自行查看源码逻辑。

参考他人文章:
1、Spring Security Oauth2关于自定义登录的几种解决方案

五、问题总结

你可能感兴趣的:(java,spring,cloud,安全,spring,boot)