若依开源框架登录使用的配置大部分都是security自定义的,目前希望在此框架基础上支持自定义的登录,如手机号+密码登录认证、手机号+短信验证码认证。
1、自定义登录实现思路
主要是实现继承DaoAuthenticationProvider类,重写additionalAuthenticationChecks方法,将通过密码标识来判断是不是需要验证密码和免密验证。
2、继承DaoAuthenticationProvider
package com.tuitui.framework.security.filter;import mon.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: -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);}@Overrideprotected 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配置
/*** 身份认证接口*/@Overrideprotected 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 mon.constant.Constants;import mon.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 -12-3 16:04*/@RestController@RequestMapping("/app")@Api(value = "AppLoginController", tags = "APP端关于登录接口")public class AppLoginController {@Autowiredprivate IAppLoginService appLoginService;/*** @Description app端手机号+密码登录* @author lst* @date -12-3 16:37* @param appUserForm app端用户密码登录表单* @return mon.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 -12-3 16:56* @param appUserCodeForm app端用户验证码登录表单* @return mon.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 -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 -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 -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 mon.constant.Constants;import mon.core.domain.model.LoginUser;import mon.exception.CustomException;import mon.exception.user.UserPasswordNotMatchException;import mon.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: -12-03 16:35*/@Servicepublic class AppLoginServiceImpl implements IAppLoginService {@Autowiredprivate TokenService tokenService;@Resourceprivate AuthenticationManager authenticationManager;/*** 免密常量*/public static final String CUSTOM_LOGIN_SMS = "CUSTOM_LOGIN_SMS";/*** @Description app端手机号+密码登录* @author lst* @date -12-3 16:37* @param appUserForm app端用户密码登录表单* @return java.lang.String*/@Overridepublic String loginByMobile(AppUserForm appUserForm) {return loginVerify(appUserForm.getMobile(),appUserForm.getPassWord());}/*** @Description 手机号+短信验证码登录* @author lst* @date -12-3 16:56* @param appUserCodeForm app端用户验证码登录表单* @return java.lang.String*/@Overridepublic String loginByCode(AppUserCodeForm appUserCodeForm) {//TODO 1、验证验校验 等接入短信在完善//TODO 2、用户验证return loginVerify(appUserCodeForm.getMobile(),Constants.CUSTOM_LOGIN_SMS);}/*** @Description 登录验证* @author lst* @date -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.loadUserByUsernameauthentication = 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、生成tokenreturn tokenService.createToken(loginUser);}}
5、postman测试
http://localhost:8080/app/login-mobile、http://localhost:8080/app/login-code
请求任一登录接口,返回token令牌
将token放入任一个接口需要登录的接口访问