若依开源框架登录使用的配置大部分都是security自定义的,目前希望在此框架基础上支持自定义的登录,如手机号+密码登录认证、手机号+短信验证码认证。

1、自定义登录实现思路

主要是实现继承DaoAuthenticationProvider类,重写additionalAuthenticationChecks方法,将通过密码标识来判断是不是需要验证密码和免密验证。

2、继承DaoAuthenticationProvider

package com.tuitui.framework.security.filter;

import com.tuitui.common.constant.Constants;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.bcrypt.BCryptPasswordEncoder;

/**
 * @description: 自定义登录扩展
 * @author: lst
 * @date: 2020-12-03 14:58
 */
public class CustomLoginAuthenticationProvider extends DaoAuthenticationProvider {
    /**
     * 免密常量
     */
    public static final String CUSTOM_LOGIN_SMS = "CUSTOM_LOGIN_SMS";

    public CustomLoginAuthenticationProvider(UserDetailsService userDetailsService) {
        super();
        setUserDetailsService(userDetailsService);
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String presentedPassword = authentication.getCredentials().toString();
            if(Constants.CUSTOM_LOGIN_SMS.equals(presentedPassword)){
                //短信登录,不验证密码

            }else{
                BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
                if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                    this.logger.debug("Authentication failed: password does not match stored value");
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }
            }
        }
    }
}

3、修改security配置

/**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        //添加自己的身份验证类,userDetailsService这个可以自己在重新定义根据那个字段验证用户存在与否
        auth.authenticationProvider(new CustomLoginAuthenticationProvider(userDetailsService));
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

并添加你的登录接口匿名访问

4、实现登录接口

controller类

package com.tuitui.web.controller.app;

import com.tuitui.api.domain.model.AppUserCodeForm;
import com.tuitui.api.domain.model.AppUserForm;
import com.tuitui.api.service.IAppLoginService;
import com.tuitui.common.constant.Constants;
import com.tuitui.common.core.domain.AjaxResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;


/**
 * @Description APP端关于登录接口
 * @author lst
 * @date 2020-12-3 16:04
 */
@RestController
@RequestMapping("/app")
@Api(value = "AppLoginController", tags = "APP端关于登录接口")
public class AppLoginController {

    @Autowired
    private IAppLoginService appLoginService;

    /**
     * @Description  app端手机号+密码登录
     * @author lst
     * @date 2020-12-3 16:37
     * @param appUserForm app端用户密码登录表单
     * @return com.tuitui.common.core.domain.AjaxResult
     */
    @PostMapping(value = "/login-mobile", produces = "application/json; charset=utf-8")
    @ApiOperation(value = "手机号+密码登录", notes = "手机号+密码登录", produces = "application/json")
    public AjaxResult loginByMobile(@RequestBody @Valid AppUserForm appUserForm) {
        AjaxResult ajax = AjaxResult.success();
        String token = appLoginService.loginByMobile(appUserForm);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    /**
     * @Description 手机号+短信验证码登录
     * @author lst
     * @date 2020-12-3 16:56
     * @param appUserCodeForm app端用户验证码登录表单
     * @return com.tuitui.common.core.domain.AjaxResult
     */
    @PostMapping(value = "/login-code", produces = "application/json; charset=utf-8")
    @ApiOperation(value = "手机号+短信验证码登录", notes = "手机号+短信验证码登录", produces = "application/json")
    public AjaxResult loginByCode(@RequestBody @Valid AppUserCodeForm appUserCodeForm) {
        AjaxResult ajax = AjaxResult.success();
        String token = appLoginService.loginByCode(appUserCodeForm);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }
}

请求表单AppUserForm、AppUserCodeForm类

package com.tuitui.api.domain.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @Description app端用户密码登录表单
 * @author lst
 * @date 2020-12-3 16:04
 */
@Data
@NotNull
@ApiModel(description = "app端用户密码登录表单")
public class AppUserForm implements Serializable {

    /**
     * 手机号码-对应系统表的username
     */
    @ApiModelProperty(name = "mobile", notes = "手机号码",required = true)
    @NotEmpty(message = "手机号码不能为空")
    private String mobile;

    /**
     * 用户密码
     */
    @ApiModelProperty(name = "passWord", notes = "用户密码",required = true)
    @NotEmpty(message = "用户密码不能为空")
    private String passWord;
}
package com.tuitui.api.domain.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @Description app端用户验证码登录表单
 * @author lst
 * @date 2020-12-3 16:04
 */
@Data
@NotNull
@ApiModel(description = "app端用户验证码登录表单")
public class AppUserCodeForm implements Serializable {

    /**
     * 手机号码-对应系统表的username
     */
    @ApiModelProperty(name = "mobile", notes = "手机号码",required = true)
    @NotEmpty(message = "手机号码不能为空")
    private String mobile;

    /**
     * 短信验证码
     */
    @ApiModelProperty(name = "smsCode", notes = "短信验证码",required = true)
    @NotEmpty(message = "短信验证码不能为空")
    private String smsCode;
}
IAppLoginService接口
package com.tuitui.api.service;

import com.tuitui.api.domain.model.AppUserCodeForm;
import com.tuitui.api.domain.model.AppUserForm;

/**
 * @DESCRIPTION APP端关于登录接口
 * @author lst
 * @date 2020-12-3 16:04
 */
public interface IAppLoginService {

    String loginByMobile(AppUserForm appUserForm);

    String loginByCode(AppUserCodeForm appUserCodeForm);
}
AppLoginServiceImpl实现类
package com.tuitui.api.service.impl;

import com.tuitui.api.domain.model.AppUserCodeForm;
import com.tuitui.api.domain.model.AppUserForm;
import com.tuitui.api.service.IAppLoginService;
import com.tuitui.common.constant.Constants;
import com.tuitui.common.core.domain.model.LoginUser;
import com.tuitui.common.exception.CustomException;
import com.tuitui.common.exception.user.UserPasswordNotMatchException;
import com.tuitui.common.utils.MessageUtils;
import com.tuitui.framework.manager.AsyncManager;
import com.tuitui.framework.manager.factory.AsyncFactory;
import com.tuitui.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @description: APP端关于登录接口实现类
 * @author: lst
 * @date: 2020-12-03 16:35
 */
@Service
public class AppLoginServiceImpl implements IAppLoginService {

    @Autowired
    private TokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    /**
     * 免密常量
     */
    public static final String CUSTOM_LOGIN_SMS = "CUSTOM_LOGIN_SMS";

    /**
     * @Description  app端手机号+密码登录
     * @author lst
     * @date 2020-12-3 16:37
     * @param appUserForm app端用户密码登录表单
     * @return java.lang.String
     */
    @Override
    public String loginByMobile(AppUserForm appUserForm) {
        return loginVerify(appUserForm.getMobile(),appUserForm.getPassWord());
    }

    /**
     * @Description 手机号+短信验证码登录
     * @author lst
     * @date 2020-12-3 16:56
     * @param appUserCodeForm app端用户验证码登录表单
     * @return java.lang.String
     */
    @Override
    public String loginByCode(AppUserCodeForm appUserCodeForm) {
        //TODO 1、验证验校验  等接入短信在完善

        //TODO 2、用户验证
        return loginVerify(appUserCodeForm.getMobile(),Constants.CUSTOM_LOGIN_SMS);
    }

    /**
     * @Description 登录验证
     * @author lst
     * @date 2020-12-3 16:47
     * @param userName 用户名
     * @param passWord 密码
     * @return java.lang.String
     */
    private String loginVerify(String userName,String passWord) {
        //TODO 1、用户验证
        Authentication authentication = null;
        try {
            //TODO 1.1该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, passWord));
        } 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 CustomException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //TODO 2、生成token
        return tokenService.createToken(loginUser);
    }

}

5、postman测试

http://localhost:8080/app/login-mobilehttp://localhost:8080/app/login-code

请求任一登录接口,返回token令牌

将token放入任一个接口需要登录的接口访问

 

 

 

Logo

快速构建 Web 应用程序

更多推荐