若依开源框架登录使用的配置大部分都是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测试
:8080/app/login-mobile、:8080/app/login-code
请求任一登录接口,返回token令牌
将token放入任一个接口需要登录的接口访问