首页 > 其他分享 >若依--自定义loadUserByUsername参数入参

若依--自定义loadUserByUsername参数入参

时间:2023-08-01 23:12:39浏览次数:47  
标签:username 自定义 -- private 入参 authentication user passwordEncoder public

若依--自定义loadUserByUsername参数入参

前言

在使用若依的Security的登录认证时,默认只能使用用户名去查询sysUser,当我需要额外的参数去查询用户数据时,只能将用户名和额外参数组成json或者特定字符拼接,然后在UserDetailsServiceImplloadUserByUsername方法自定义查询数据。但是似乎不太优雅。

代码如下

SysLoginService

登录service层的代码,使用自定义的CustomUsernamePasswordAuthenticationToken,调用clientLoginAuthProvider自定义实现类的认证;

......
    @Resource
    private ClientLoginAuthProvider clientLoginAuthProvider;
public String appLogin(String username, String password, Long schoolId) {
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        //userService.checkStudent(username);
        try
        {
            CustomUsernamePasswordAuthenticationToken authenticationToken = new CustomUsernamePasswordAuthenticationToken(username, password,schoolId);
            //UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用ClientUserDetailsService.loadUserByUsername
            authentication = clientLoginAuthProvider.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }
......

一、CustomUsernamePasswordAuthenticationToken

public class CustomUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
    private final Long schoolId;

    public CustomUsernamePasswordAuthenticationToken(Object principal, Object credentials, Long schoolId) {
        super(principal, credentials);
        this.schoolId = schoolId;
    }

    public Long getSchoolId() {
        return schoolId;
    }
}

二、自定义ClientUserDetailsService 以及实现类ClientUserDetailsServiceImpl

接口:

public interface ClientUserDetailsService{
    UserDetails loadUserByUsername(String username,Long SchoolId) throws UsernameNotFoundException;
}

实现类:

@Service("clientUserDetailsService")
public class ClientUserDetailsServiceImpl implements ClientUserDetailsService{
    private static final Logger log = LoggerFactory.getLogger(ClientUserDetailsServiceImpl.class);
    @Resource
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysPasswordService passwordService;
    @Autowired
    private SysPermissionService permissionService;
    @Override
    public UserDetails loadUserByUsername(String username, Long schoolId) throws UsernameNotFoundException {
        // 此处即可自定义查询用户
        SysUser user = sysUserMapper.getStudentByUserName(username,schoolId);
        if (StringUtils.isNull(user))
        {
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException("登录用户:" + username + " 不存在");
        }
        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        }
        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            log.info("登录用户:{} 已被停用.", username);
            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
        }

        passwordService.validate(user);

        return createLoginUser(user);
    }

    public UserDetails createLoginUser(SysUser user)
    {
        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
    }
}

三、ClientLoginAuthProvider

内容和DaoAuthenticationProvider中的一样 只是添加了ClientUserDetailsService,在retrieveUser方法中调用我们自己的实现方法

public class ClientLoginAuthProvider extends AbstractUserDetailsAuthenticationProvider {


    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";

    private PasswordEncoder passwordEncoder;

    /**
     * The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}
     * on when the user is not found to avoid SEC-2056. This is necessary, because some
     * {@link PasswordEncoder} implementations will short circuit if the password is not
     * in a valid format.
     */
    private volatile String userNotFoundEncodedPassword;

    private ClientUserDetailsService userDetailsService;

    private UserDetailsPasswordService userDetailsPasswordService;

    public ClientLoginAuthProvider(ClientUserDetailsService clientUserDetailsService) {
        this.userDetailsService = clientUserDetailsService;
        setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
    }

    @Override
    @SuppressWarnings("deprecation")
    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            this.logger.debug("Failed to authenticate since password does not match stored value");
            throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }

    @Override
    protected void doAfterPropertiesSet() {
        Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
    }

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        CustomUsernamePasswordAuthenticationToken auth = (CustomUsernamePasswordAuthenticationToken) authentication;

        //多个参数
        UserDetails loadedUser = userDetailsService.loadUserByUsername(username,auth.getSchoolId());

        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException(
                    "UserDetailsService returned null, which is an interface contract violation");
        }
        return loadedUser;
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
                                                         UserDetails user) {
        boolean upgradeEncoding = this.userDetailsPasswordService != null
                && this.passwordEncoder.upgradeEncoding(user.getPassword());
        if (upgradeEncoding) {
            String presentedPassword = authentication.getCredentials().toString();
            String newPassword = this.passwordEncoder.encode(presentedPassword);
            user = this.userDetailsPasswordService.updatePassword(user, newPassword);
        }
        return super.createSuccessAuthentication(principal, authentication, user);
    }

    private void prepareTimingAttackProtection() {
        if (this.userNotFoundEncodedPassword == null) {
            this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
        }
    }

    private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
        if (authentication.getCredentials() != null) {
            String presentedPassword = authentication.getCredentials().toString();
            this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
        }
    }

    /**
     * Sets the PasswordEncoder instance to be used to encode and validate passwords. If
     * not set, the password will be compared using
     * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
     * @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
     * types.
     */
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
        this.passwordEncoder = passwordEncoder;
        this.userNotFoundEncodedPassword = null;
    }

    protected PasswordEncoder getPasswordEncoder() {
        return this.passwordEncoder;
    }

    public void setUserDetailsService(ClientUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    protected ClientUserDetailsService getUserDetailsService() {
        return this.userDetailsService;
    }

    public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
        this.userDetailsPasswordService = userDetailsPasswordService;
    }

}

四、SecurityConfig

然后在SecurityConfig中添加ClientUserDetailsService注入,并且把ClientLoginAuthProvider注入到容器中

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    ......
    @Resource
    private ClientUserDetailsService clientUserDetailsService;
    
    ......

    @Bean
    public ClientLoginAuthProvider clientLoginAuthProvider(){
        ClientLoginAuthProvider provider = new ClientLoginAuthProvider(clientUserDetailsService);
        provider.setPasswordEncoder(bCryptPasswordEncoder());
        return provider;
    }

}

然后我们原来的接口能登录,新添加的接口也能登录了。

标签:username,自定义,--,private,入参,authentication,user,passwordEncoder,public
From: https://www.cnblogs.com/hwjShl/p/17599370.html

相关文章

  • Docker简易项目部署
    Docker简易项目部署私有仓库#dockerpull拉取,都是从hub.docker上拉取的,公司自己做的docker镜像,放在哪比较合适#dockerpush推到hub.docker,如果是公开,大家都可以下载,公司内部只希望公司的人用,其他人不能用#公司内部,需要有个仓库管理自己的镜像----》私有仓库私服 ......
  • css伪类选择器大全
    伪类选择器css伪类选择器大全:https://developer.mozilla.org/zh-CN/docs/Web/CSS/Pseudo-classes伪类含义:link选中未访问过的超链接:visited选中已访问过的超链接:hover选中鼠标移入的元素:active选中鼠标按下的元素:focus选中聚焦的表单元素:......
  • 2023.8.1
    今天看SROP的时候,突然想到,既然ctfwiki上讲的不是很详细,搜其他SROP的博客,里面讲的例题又和ctfwiki上的不是很一样,仅有一点参考价值。那我为什么不能直接去搜ctfwiki加上SROP这两个关键词呢?这么一搜,果然找到了符合ctfwiki上内容,又有详细解读的博客,找到的第一篇里还是有点不够详细,找......
  • Java面试题 P35:数据库篇:MySql篇-事务-事务的特性是什么?
       ......
  • Java将CST的时间字符串转换成需要的日期格式字符串
    ‘CannotformatgivenObjectasaDate’翻译出来就是:无法将给定对象格式化为日期一般的显示当前时间都是SimpleDateFormatdf=newSimpleDateFormat("yyyyMMdd");Datedate=newDate();stringstring=df.format(date); 可是这次咋咋的都报这个错查了又查,网上都......
  • 【设计模式】适配器模式Adapter:处理不同 API 接口的兼容性
    (目录)适配器模式适配器模式(AdapterPattern)是作为两个不兼容的接⼝之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独⽴接⼝的功能。在某些时候,客户期望获得某种功能接⼝但现有的接⼝⽆法满⾜客户的需求,例如美国的正常供电电压为110V,⼀个中国⼈带了⼀款中国制造......
  • Python超实用!批量重命名文件/文件夹,只需1行代码
    大家好,这里是程序员晚枫,之前在小破站给大家分享了一个视频:批量重命名文件。最近在程序员晚枫的读者群里,发现很多朋友对这个功能很感兴趣,尤其是对下一步的优化:批量重命名文件夹。这周我利用下班时间,把这个功能改进了一下,增加了几个可选的功能,我们一起来看一下吧~批量重命名文件......
  • Spring框架你了解多少?
    Spring框架是当前Java领域应用最广的框架,它之所以那么成功,这主要是得益于它的设计理念。它的理念包括IoC(InversionofControl,控制反转)和AOP(AspectOrientedProgramming,面向切面编程)。下面我们就来一起学习下Spring这个优秀的开源框架。什么是Spring?Spring是分层......
  • 2023年7月文章一览
    受台风“杜苏芮”的残余环流影响,雨从7月下到了8月。2023年7月总共更新了4篇文章,如下:1.VisualStudioCode错误:Cannotbuildanddebugbecausethe.....2.C语言实现解一元二次方程3.《ProgrammingAbstractionsInC》阅读笔记p69-p714.VSCode同时编译多个C文件后续因为“字符串数......
  • 淘宝京东拼多多抖音淘特1688苏宁等平台商品详情API接口
    商品详情页面数据接口,商品销量接口,关键词搜索商品销量接口,商品APP详情API接口数据代码如下:公共参数请求地址: console.open.onebound.cn/console/?i=…名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥api_nameString是API接口名称(包括在请求地......