springboot security 登录(含账号、短信登录、第三方登录)模块
This commit is contained in:
parent
7fb712c596
commit
35d4a6be84
|
@ -92,6 +92,30 @@
|
|||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- HikariCP数据库连接池(JDK1.8) -->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- springboot mybatis-plus-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 内存数据库h2-->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -68,7 +68,7 @@ public class OssFacade {
|
|||
}
|
||||
|
||||
private OSSClient ossClient() {
|
||||
return new OSSClient(properties.getEndpoint(), properties.getAccessKeyID(), properties.getAccessKeySecret(),
|
||||
return new OSSClient(properties.getEndpoint(), properties.getAccessKeyId(), properties.getAccessKeySecret(),
|
||||
configuration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,4 +63,5 @@ public class OssProperties {
|
|||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -69,4 +69,25 @@ public class BizConsts {
|
|||
*/
|
||||
public static String BASE_NOT_LOGIN_CODE = BASE_CODE + "010";
|
||||
public static String BASE_NOT_LOGIN_MSG = "您尚未登陆";
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 用户登录发送短信验证码
|
||||
*/
|
||||
public static String CONSUMER_LOGIN_SMS = "consumer.login.sendSms";
|
||||
/**
|
||||
* 用户绑定手机号
|
||||
*/
|
||||
public static String CONSUMER_BIND_MOBILE = "consumer.bind.mobile.sms";
|
||||
/**
|
||||
* 用户登录信息
|
||||
*/
|
||||
public static String CONSUMER_LOGIN_REDIS = "consumer.login.redis.session";
|
||||
|
||||
/**
|
||||
* 后台管理员登录信息
|
||||
*/
|
||||
public static String ADMIN_LOGIN_REDIS = "admin.login.redis.session";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.yexuejc.springboot.base.constant;
|
||||
|
||||
/**
|
||||
* 注册类型
|
||||
* @author: maxf
|
||||
* @date: 2018/8/6 21:43:58
|
||||
*/
|
||||
public class DictRegTypeConsts {
|
||||
/**
|
||||
* A : 账号注册
|
||||
*/
|
||||
public static final String DICT_ACCOUNT = "A";
|
||||
/**
|
||||
* B : 微博注册
|
||||
*/
|
||||
public static final String DICT_WEIBO = "B";
|
||||
/**
|
||||
* Q : qq注册
|
||||
*/
|
||||
public static final String DICT_QQ = "Q";
|
||||
/**
|
||||
* S : 手机号注册
|
||||
*/
|
||||
public static final String DICT_MOBILE = "S";
|
||||
/**
|
||||
* W : 微信注册
|
||||
*/
|
||||
public static final String DICT_WECHAT = "W";
|
||||
|
||||
private DictRegTypeConsts() {
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.yexuejc.springboot.base.constant;
|
||||
|
||||
/**
|
||||
* 登录方式
|
||||
*
|
||||
* @author: maxf
|
||||
* @date: 2018/9/9 12:22:53
|
||||
*/
|
||||
public class LogTypeConsts {
|
||||
|
||||
/**
|
||||
* 短信登录
|
||||
*/
|
||||
public static final String SMS = "sms";
|
||||
/**
|
||||
* QQ 登录
|
||||
*/
|
||||
public static final String QQ = "qq";
|
||||
/**
|
||||
* 微信
|
||||
*/
|
||||
public static final String WECHAT = "wechat";
|
||||
/**
|
||||
* 微博
|
||||
*/
|
||||
public static final String WEIBO = "weibo";
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
public static final String ACCOUNT = "account";
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.yexuejc.springboot.base.exception;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* 第三方授权异常
|
||||
*
|
||||
* @author: maxf
|
||||
* @date: 2018/5/27 19:20
|
||||
*/
|
||||
public class ThirdPartyAuthorizationException extends AuthenticationException {
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private String code = "E";
|
||||
|
||||
public ThirdPartyAuthorizationException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
public ThirdPartyAuthorizationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ThirdPartyAuthorizationException() {
|
||||
super("授权异常");
|
||||
}
|
||||
|
||||
public ThirdPartyAuthorizationException(Throwable t) {
|
||||
super("授权异常", t);
|
||||
}
|
||||
|
||||
|
||||
public ThirdPartyAuthorizationException(String code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
public ThirdPartyAuthorizationException(String code, String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.springboot.base.constant.LogTypeConsts;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ConsumerAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
|
||||
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
|
||||
/**
|
||||
* 登录方式
|
||||
*/
|
||||
public static final String SPRING_SECURITY_FORM_LOGTYPE_KEY = "logtype";
|
||||
public static final String SPRING_SECURITY_FORM_OPENID_KEY = "openid";
|
||||
/********************************** 第三方登录时附带信息*************************************/
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
public static final String SPRING_SECURITY_FORM_HEAD_KEY = "head";
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
public static final String SPRING_SECURITY_FORM_NICKNAME_KEY = "nickname";
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
public static final String SPRING_SECURITY_FORM_SEX_KEY = "sex";
|
||||
/********************************** 第三方登录时附带信息*************************************/
|
||||
|
||||
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
|
||||
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
|
||||
private String logtypeParameter = SPRING_SECURITY_FORM_LOGTYPE_KEY;
|
||||
private String openidParameter = SPRING_SECURITY_FORM_OPENID_KEY;
|
||||
private String headParameter = SPRING_SECURITY_FORM_HEAD_KEY;
|
||||
private String nicknameParameter = SPRING_SECURITY_FORM_NICKNAME_KEY;
|
||||
private String sexParameter = SPRING_SECURITY_FORM_SEX_KEY;
|
||||
private boolean postOnly = true;
|
||||
private boolean reverse = true;
|
||||
|
||||
// ~ Constructors
|
||||
// ===================================================================================================
|
||||
|
||||
protected ConsumerAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
|
||||
super(new AntPathRequestMatcher("/login", "POST"));
|
||||
setAuthenticationManager(authenticationManager);
|
||||
}
|
||||
|
||||
protected ConsumerAuthenticationProcessingFilter() {
|
||||
super(new AntPathRequestMatcher("/login", "POST"));
|
||||
}
|
||||
// ~ Methods
|
||||
// ========================================================================================================
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response) throws AuthenticationException {
|
||||
return displyAuthentication(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可继承自定义处理登录请求
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
protected Authentication displyAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
if (postOnly && !request.getMethod().equals("POST")) {
|
||||
throw new AuthenticationServiceException(
|
||||
"Authentication method not supported: " + request.getMethod());
|
||||
}
|
||||
String logtype = obtainLogtype(request);
|
||||
System.out.println("登录方式:" + logtype);
|
||||
String username = "";
|
||||
String password = "";
|
||||
if (logtype == null) {
|
||||
logtype = "";
|
||||
}
|
||||
String openid = "";
|
||||
String smscode = "";
|
||||
/**第三方登录:微信 用户头像*/
|
||||
String head = "";
|
||||
String nickname = "";
|
||||
String sex = "";
|
||||
//根据不同登录方式做不同处理
|
||||
getParams(request, logtype, username, password, smscode, openid, sex, head, nickname);
|
||||
UsernamePasswordAuthenticationToken authRequest = new ConsumerToken(
|
||||
logtype, smscode, openid, username, password, head, nickname, sex);
|
||||
|
||||
// Allow subclasses to set the "details" property
|
||||
setDetails(request, authRequest);
|
||||
//ProviderManager 这东西会先把DaoAuthenticationProvider加入列表,导致setHideUserNotFoundExceptions(false);被覆盖
|
||||
if (this.getAuthenticationManager() instanceof ProviderManager && reverse) {
|
||||
reverse = false;
|
||||
ProviderManager providerManager = (ProviderManager) this.getAuthenticationManager();
|
||||
providerManager.getProviders().forEach(it -> {
|
||||
((AbstractUserDetailsAuthenticationProvider) it).setHideUserNotFoundExceptions(false);
|
||||
});
|
||||
Collections.reverse(providerManager.getProviders());
|
||||
}
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据登录方式获取请求参数
|
||||
*
|
||||
* @param request 登录请求
|
||||
* @param logtype 登录类型
|
||||
* @param username 账号
|
||||
* @param password 密码
|
||||
* @param smscode 短信验证码
|
||||
* @param openid 第三封授权id
|
||||
* @param sex 附加:性别
|
||||
* @param head 附加:头像(源头像路径)
|
||||
* @param nickname 附加:昵称
|
||||
*/
|
||||
protected void getParams(HttpServletRequest request, String logtype, String username, String password,
|
||||
String smscode, String openid, String sex, String head, String nickname) {
|
||||
switch (logtype) {
|
||||
case LogTypeConsts.SMS:
|
||||
//短信登录
|
||||
username = obtainUsername(request);
|
||||
smscode = obtainPassword(request);
|
||||
break;
|
||||
case LogTypeConsts.QQ:
|
||||
//QQ登录
|
||||
openid = obtainOpenid(request);
|
||||
head = obtainHead(request);
|
||||
nickname = obtainNickname(request);
|
||||
sex = obtainSex(request);
|
||||
break;
|
||||
case LogTypeConsts.WECHAT:
|
||||
//微信登录
|
||||
openid = obtainOpenid(request);
|
||||
head = obtainHead(request);
|
||||
nickname = obtainNickname(request);
|
||||
sex = obtainSex(request);
|
||||
break;
|
||||
case LogTypeConsts.WEIBO:
|
||||
//微博登录
|
||||
openid = obtainOpenid(request);
|
||||
head = obtainHead(request);
|
||||
nickname = obtainNickname(request);
|
||||
sex = obtainSex(request);
|
||||
break;
|
||||
default:
|
||||
//默认账号+密码登录
|
||||
username = obtainUsername(request).trim();
|
||||
password = obtainPassword(request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables subclasses to override the composition of the password, such as by
|
||||
* including additional values and a separator.
|
||||
* <p>
|
||||
* This might be used for example if a postcode/zipcode was required in addition to
|
||||
* the password. A delimiter such as a pipe (|) should be used to separate the
|
||||
* password and extended value(s). The <code>AuthenticationDao</code> will need to
|
||||
* generate the expected password in a corresponding manner.
|
||||
* </p>
|
||||
*
|
||||
* @param request so that request attributes can be retrieved
|
||||
* @return the password that will be presented in the <code>Authentication</code>
|
||||
* request token to the <code>AuthenticationManager</code>
|
||||
*/
|
||||
protected String obtainPassword(HttpServletRequest request) {
|
||||
return request.getParameter(passwordParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables subclasses to override the composition of the username, such as by
|
||||
* including additional values and a separator.
|
||||
*
|
||||
* @param request so that request attributes can be retrieved
|
||||
* @return the username that will be presented in the <code>Authentication</code>
|
||||
* request token to the <code>AuthenticationManager</code>
|
||||
*/
|
||||
protected String obtainUsername(HttpServletRequest request) {
|
||||
return request.getParameter(usernameParameter);
|
||||
}
|
||||
|
||||
protected String obtainLogtype(HttpServletRequest request) {
|
||||
return request.getParameter(logtypeParameter);
|
||||
}
|
||||
|
||||
protected String obtainOpenid(HttpServletRequest request) {
|
||||
return request.getParameter(openidParameter);
|
||||
}
|
||||
|
||||
protected String obtainHead(HttpServletRequest request) {
|
||||
return request.getParameter(headParameter);
|
||||
}
|
||||
|
||||
protected String obtainNickname(HttpServletRequest request) {
|
||||
return request.getParameter(nicknameParameter);
|
||||
}
|
||||
|
||||
protected String obtainSex(HttpServletRequest request) {
|
||||
return request.getParameter(sexParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provided so that subclasses may configure what is put into the authentication
|
||||
* request's details property.
|
||||
*
|
||||
* @param request that an authentication request is being created for
|
||||
* @param authRequest the authentication request object that should have its details
|
||||
* set
|
||||
*/
|
||||
protected void setDetails(HttpServletRequest request,
|
||||
UsernamePasswordAuthenticationToken authRequest) {
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parameter name which will be used to obtain the username from the login
|
||||
* request.
|
||||
*
|
||||
* @param usernameParameter the parameter name. Defaults to "username".
|
||||
*/
|
||||
public void setUsernameParameter(String usernameParameter) {
|
||||
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
|
||||
this.usernameParameter = usernameParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parameter name which will be used to obtain the password from the login
|
||||
* request..
|
||||
*
|
||||
* @param passwordParameter the parameter name. Defaults to "password".
|
||||
*/
|
||||
public void setPasswordParameter(String passwordParameter) {
|
||||
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
|
||||
this.passwordParameter = passwordParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines whether only HTTP POST requests will be allowed by this filter. If set to
|
||||
* true, and an authentication request is received which is not a POST request, an
|
||||
* exception will be raised immediately and authentication will not be attempted. The
|
||||
* <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
|
||||
* authentication.
|
||||
* <p>
|
||||
* Defaults to <tt>true</tt> but may be overridden by subclasses.
|
||||
*/
|
||||
public void setPostOnly(boolean postOnly) {
|
||||
this.postOnly = postOnly;
|
||||
}
|
||||
|
||||
public final String getUsernameParameter() {
|
||||
return usernameParameter;
|
||||
}
|
||||
|
||||
public final String getPasswordParameter() {
|
||||
return passwordParameter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.base.pojo.ApiVO;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
import com.yexuejc.springboot.base.constant.BizConsts;
|
||||
import com.yexuejc.springboot.base.constant.LogTypeConsts;
|
||||
import com.yexuejc.springboot.base.exception.ThirdPartyAuthorizationException;
|
||||
import com.yexuejc.springboot.base.security.inte.User;
|
||||
import com.yexuejc.springboot.base.security.inte.UserService;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
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.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.*;
|
||||
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <h2>主要做验证用户使用</h2>
|
||||
* <pre>
|
||||
* 密码登录:校验账号密码
|
||||
* 短信登录:校验短信验证码
|
||||
* 第三方登录:校验openid=>用户中心(数据库)不存在,进行新增数据操作
|
||||
* </pre>
|
||||
*
|
||||
* @author maxf
|
||||
* @version 1.0
|
||||
* @ClassName ConsumerAuthenticationProvider
|
||||
* @Description
|
||||
* @date 2018/11/9 11:23
|
||||
*/
|
||||
public class ConsumerAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
// ~ Static fields/initializers
|
||||
// =====================================================================================
|
||||
|
||||
/**
|
||||
* The plaintext password used to perform
|
||||
* PasswordEncoder#matches(CharSequence, String)} on when the user is
|
||||
* not found to avoid SEC-2056.
|
||||
*/
|
||||
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
|
||||
|
||||
// ~ Instance fields
|
||||
// ================================================================================================
|
||||
|
||||
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 UserDetailsService userDetailsService;
|
||||
private final UserService accountView;
|
||||
|
||||
|
||||
public ConsumerAuthenticationProvider(UserDetailsService userDetailsService, UserService accountView) {
|
||||
// super();
|
||||
super.setHideUserNotFoundExceptions(false);
|
||||
// // 这个地方一定要对userDetailsService赋值,不然userDetailsService是null (这个坑有点深)
|
||||
setUserDetailsService(userDetailsService);
|
||||
setPasswordEncoder(createDelegatingPasswordEncoder());
|
||||
this.accountView = accountView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义加密方式
|
||||
* 默认MD5加密
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected PasswordEncoder createDelegatingPasswordEncoder() {
|
||||
String encodingId = "MD5";
|
||||
Map<String, PasswordEncoder> encoders = new HashMap<>(9);
|
||||
encoders.put("bcrypt", new BCryptPasswordEncoder());
|
||||
encoders.put("ldap", new LdapShaPasswordEncoder());
|
||||
encoders.put("MD4", new Md4PasswordEncoder());
|
||||
encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
|
||||
encoders.put("noop", NoOpPasswordEncoder.getInstance());
|
||||
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
|
||||
encoders.put("scrypt", new SCryptPasswordEncoder());
|
||||
encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
|
||||
encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
|
||||
encoders.put("sha256", new StandardPasswordEncoder());
|
||||
return new DelegatingPasswordEncoder(encodingId, encoders);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
String requestPwd = authentication.getCredentials().toString();
|
||||
if (authentication instanceof ConsumerToken) {
|
||||
ConsumerToken consumerToken = (ConsumerToken) authentication;
|
||||
|
||||
|
||||
displyAuthenticationChecks(consumerToken, requestPwd, userDetails.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录密码校验
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @param requestPwd 登录密码
|
||||
* @param dbPwd 源用户密码(数据库中的密码)
|
||||
*/
|
||||
protected void displyAuthenticationChecks(ConsumerToken consumerToken, String requestPwd, String dbPwd) {
|
||||
ApiVO apiVO;
|
||||
switch (consumerToken.getLogtype()) {
|
||||
case LogTypeConsts.SMS:
|
||||
//新注册账号已经在注册的时候校验的短信验证码,所有这里不校验
|
||||
if (!consumerToken.isReg) {
|
||||
apiVO = accountView.checkSmsCode2Redis(BizConsts.CONSUMER_LOGIN_SMS, consumerToken.getUsername(), consumerToken.getSmscode());
|
||||
if (apiVO.isFail()) {
|
||||
throw new ThirdPartyAuthorizationException(apiVO.getMsg());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LogTypeConsts.QQ:
|
||||
break;
|
||||
case LogTypeConsts.WECHAT:
|
||||
|
||||
break;
|
||||
case LogTypeConsts.WEIBO:
|
||||
break;
|
||||
default:
|
||||
//账号登录
|
||||
if (!passwordEncoder.matches(requestPwd, "{MD5}" + dbPwd)) {
|
||||
logger.debug("Authentication failed: password does not match stored value");
|
||||
|
||||
throw new ThirdPartyAuthorizationException("密码错误");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAfterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final UserDetails retrieveUser(String username,
|
||||
UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
UserDetails loadedUser = null;
|
||||
|
||||
String logtype = "";
|
||||
ConsumerToken consumerToken = null;
|
||||
if (authentication instanceof ConsumerToken) {
|
||||
consumerToken = (ConsumerToken) authentication;
|
||||
logtype = StrUtil.setStr(consumerToken.getLogtype(), "");
|
||||
}
|
||||
try {
|
||||
//通过username查询数据库,当第三方登录时所传参数为openid(没有传username),此次会抛出找DB不到账号信息的异常,交给catch处理
|
||||
prepareTimingAttackProtection();
|
||||
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
//账号登录
|
||||
if ((StrUtil.isEmpty(logtype) || LogTypeConsts.ACCOUNT.equals(logtype))) {
|
||||
mitigateAgainstTimingAttack(authentication);
|
||||
throw notFound;
|
||||
} else {
|
||||
try {
|
||||
//其他方式登录:查询账号 没有->创建账号
|
||||
//第三方登录
|
||||
if (consumerToken != null && StrUtil.isNotEmpty(consumerToken.getOpenid())) {
|
||||
ApiVO apiVO = accountView.checkOpenId(consumerToken);
|
||||
if (apiVO.isSucc()) {
|
||||
//已有账号
|
||||
User consumer = apiVO.getObject1(User.class);
|
||||
// 处理用户权限
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
for (String role : consumer.getRoles()) {
|
||||
authorities.add(new SimpleGrantedAuthority(role));
|
||||
}
|
||||
loadedUser = new ConsumerUser(
|
||||
StrUtil.isEmpty(consumer.getMobile()) ? consumerToken.getOpenid() : consumer.getMobile(),
|
||||
consumer.getPwd(), consumer.getEnable(), consumer.getNonExpire(),
|
||||
true, consumer.getNonLock(), authorities, consumer.getConsumerId(),
|
||||
logtype, System.currentTimeMillis());
|
||||
return loadedUser;
|
||||
}
|
||||
}
|
||||
//第三方登录+短信登录
|
||||
if (consumerToken != null) {
|
||||
//没有->创建账号
|
||||
consumerToken.isReg = true;
|
||||
ApiVO apiVO = accountView.addConsumer(consumerToken);
|
||||
if (apiVO.isSucc()) {
|
||||
loadedUser = display(consumerToken, apiVO.getObject1(User.class));
|
||||
return loadedUser;
|
||||
} else {
|
||||
throw new ThirdPartyAuthorizationException(apiVO.getMsg());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (e instanceof ThirdPartyAuthorizationException) {
|
||||
throw e;
|
||||
}
|
||||
throw new ThirdPartyAuthorizationException("登录失败,请稍后重试");
|
||||
}
|
||||
throw notFound;
|
||||
}
|
||||
} catch (Exception repositoryProblem) {
|
||||
throw new InternalAuthenticationServiceException(
|
||||
repositoryProblem.getMessage(), repositoryProblem);
|
||||
}
|
||||
if (loadedUser == null) {
|
||||
throw new InternalAuthenticationServiceException(
|
||||
"UserDetailsService returned null, which is an interface contract violation");
|
||||
}
|
||||
return loadedUser;
|
||||
}
|
||||
|
||||
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, "{MD5}" + this.userNotFoundEncodedPassword);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户为登录用户
|
||||
*
|
||||
* @param consumerToken 登录用户信息
|
||||
* @param consumer 实际用户信息
|
||||
* @return response User
|
||||
*/
|
||||
private UserDetails display(ConsumerToken consumerToken, User consumer) {
|
||||
// 处理用户权限
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
for (String role : consumer.getRoles()) {
|
||||
authorities.add(new SimpleGrantedAuthority(role));
|
||||
}
|
||||
UserDetails userDetails = new ConsumerUser(
|
||||
StrUtil.isEmpty(consumer.getMobile()) ? consumerToken.getOpenid() : consumer.getMobile(),
|
||||
consumer.getPwd(), consumer.getEnable(), consumer.getNonExpire(),
|
||||
true, consumer.getNonLock(), authorities, consumer.getConsumerId(),
|
||||
consumerToken.getLogtype(), System.currentTimeMillis());
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 passwordEncoder;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
protected UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.base.constant.RespsConsts;
|
||||
import com.yexuejc.base.util.JwtUtil;
|
||||
import com.yexuejc.springboot.base.constant.BizConsts;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Security 上下文配置
|
||||
* <p>
|
||||
* 校验token=>返回token携带用户(登录用户)信息
|
||||
* </p>
|
||||
*
|
||||
* @ClassName: ConsumerSecurityContextRepository
|
||||
* @Description:Security 上下文配置
|
||||
* @author: maxf
|
||||
* @date: 2017年11月22日 下午4:39:20
|
||||
*/
|
||||
public class ConsumerSecurityContextRepository implements SecurityContextRepository {
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
private static final String TOKEN = "token";
|
||||
private static final String ROLES = "roles";
|
||||
|
||||
private final RedisTemplate<Object, Object> redisTemplate0;
|
||||
|
||||
public ConsumerSecurityContextRepository(RedisTemplate<Object, Object> redisTemplate0) {
|
||||
this.redisTemplate0 = redisTemplate0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
|
||||
SecurityContext context = readSecurityContext(requestResponseHolder.getRequest());
|
||||
if (context == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No SecurityContext was available, A new one will be created.");
|
||||
}
|
||||
context = SecurityContextHolder.createEmptyContext();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the security context on completion of a request.
|
||||
* <p>Title: saveContext</p>
|
||||
* <p>Description:Stores the security context on completion of a request. </p>
|
||||
*
|
||||
* @param context
|
||||
* @param request
|
||||
* @param response
|
||||
* @see SecurityContextRepository#saveContext(SecurityContext, HttpServletRequest, HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsContext(HttpServletRequest request) {
|
||||
return readSecurityContext(request) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 过滤登录认证
|
||||
* 默认:header=>Authorization:token
|
||||
* token 为 jwt串
|
||||
* 此处使用登录返回的token解析到具体的登录用户,返回登录用户信息;如果没有,者无权限请求该接口
|
||||
* </pre>
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
protected SecurityContext readSecurityContext(HttpServletRequest request) {
|
||||
final boolean debug = logger.isDebugEnabled();
|
||||
|
||||
String token = request.getHeader(RespsConsts.HEADER_AUTHORIZATION);
|
||||
if (token == null || token.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
LoginToken consumerToken = JwtUtil.instace().parse(token, LoginToken.class);
|
||||
if (consumerToken == null || consumerToken.getUsername() == null || consumerToken.getUsername().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
// 根据token中携带的username查询用户信息
|
||||
Map<Object, Object> entry = redisTemplate0.opsForHash()
|
||||
.entries(BizConsts.CONSUMER_LOGIN_REDIS + "." + consumerToken.getUsername());
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
if (!token.equals(entry.get(TOKEN))) {
|
||||
return null;
|
||||
}
|
||||
// 处理用户权限
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
for (String role : (List<String>) entry.get(ROLES)) {
|
||||
authorities.add(new SimpleGrantedAuthority(role));
|
||||
}
|
||||
ConsumerUser consumerUser = new ConsumerUser((String) entry.get("username"), "",
|
||||
true, true, true, true, authorities,
|
||||
(String) entry.get("id"), (String) entry.get("logType"), (Long) entry.get("logTime"));
|
||||
|
||||
SecurityContext context = new SecurityContextImpl();
|
||||
|
||||
consumerUser.eraseCredentials();
|
||||
context.setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken(consumerUser, null, consumerUser.getAuthorities()));
|
||||
|
||||
if (debug) {
|
||||
logger.debug("Obtained a valid SecurityContext : '" + context + "'");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.base.util.JsonUtil;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 登录参数 request <br/>
|
||||
* 用于存放授权后,需要提取的用户信息
|
||||
*
|
||||
* @ClassName: ConsumerToken
|
||||
* @Description: 用于存放授权后,需要提取的用户信息
|
||||
* @author: maxf
|
||||
* @date: 2017年11月22日 下午4:39:29
|
||||
*/
|
||||
public class ConsumerToken extends UsernamePasswordAuthenticationToken {
|
||||
private static final long serialVersionUID = -1923797941L;
|
||||
|
||||
|
||||
/**
|
||||
* 消费者用户名(手机号)
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 登录方式
|
||||
*/
|
||||
private String logtype;
|
||||
/**
|
||||
* 短信验证码
|
||||
*/
|
||||
private String smscode;
|
||||
/**
|
||||
* openid
|
||||
*/
|
||||
private String openid;
|
||||
/********************************** 第三方登录时附带信息*************************************/
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String head;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
private String sex;
|
||||
|
||||
|
||||
/********************************** 第三方登录时附带信息*************************************/
|
||||
|
||||
/**
|
||||
* 是否注册账号:减少sms注册时短信校验次数
|
||||
*/
|
||||
public boolean isReg = false;
|
||||
|
||||
|
||||
public ConsumerToken(String username) {
|
||||
super(null, null);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public ConsumerToken(Object principal, Object credentials) {
|
||||
super(principal, credentials);
|
||||
}
|
||||
|
||||
public ConsumerToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(principal, credentials, authorities);
|
||||
}
|
||||
|
||||
public ConsumerToken(String logtype, String smscode, String openid, String username, Object credentials, String head, String nickname,
|
||||
String sex) {
|
||||
super(username, credentials);
|
||||
this.username = username;
|
||||
this.logtype = logtype;
|
||||
this.smscode = smscode;
|
||||
this.openid = openid;
|
||||
this.head = head;
|
||||
this.nickname = nickname;
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonUtil.obj2Json(this);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getLogtype() {
|
||||
return logtype;
|
||||
}
|
||||
|
||||
public void setLogtype(String logtype) {
|
||||
this.logtype = logtype;
|
||||
}
|
||||
|
||||
public String getSmscode() {
|
||||
return smscode;
|
||||
}
|
||||
|
||||
public void setSmscode(String smscode) {
|
||||
this.smscode = smscode;
|
||||
}
|
||||
|
||||
public String getOpenid() {
|
||||
return openid;
|
||||
}
|
||||
|
||||
public void setOpenid(String openid) {
|
||||
this.openid = openid;
|
||||
}
|
||||
|
||||
public String getHead() {
|
||||
return head;
|
||||
}
|
||||
|
||||
public void setHead(String head) {
|
||||
this.head = head;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(String sex) {
|
||||
this.sex = sex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 登录用户信息 response
|
||||
*
|
||||
* @ClassName: ConsumerUser
|
||||
* @Description:登录用户信息
|
||||
* @author: maxf
|
||||
* @date: 2017年11月22日 下午4:39:45
|
||||
*/
|
||||
public class ConsumerUser extends User {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消费者用户主键ID
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 登录方式
|
||||
*/
|
||||
private String logType;
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
private Long logTime;
|
||||
|
||||
public ConsumerUser(String username, String password, boolean enabled, boolean accountNonExpired,
|
||||
boolean credentialsNonExpired, boolean accountNonLocked,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
String id, String logType, Long logTime) {
|
||||
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
|
||||
this.id = id;
|
||||
this.logType = logType;
|
||||
this.logTime = logTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅做保存登录用户信息时使用
|
||||
*
|
||||
* @param username
|
||||
* @param id
|
||||
* @param logType
|
||||
* @param logTime
|
||||
*/
|
||||
public ConsumerUser(String username, String id, String logType, Long logTime) {
|
||||
super(username, "", true, true, true, true, null);
|
||||
this.id = id;
|
||||
this.logType = logType;
|
||||
this.logTime = logTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString()).append(": ");
|
||||
|
||||
sb.append("id: ").append(this.id).append("; ");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLogType() {
|
||||
return logType;
|
||||
}
|
||||
|
||||
public void setLogType(String logType) {
|
||||
this.logType = logType;
|
||||
}
|
||||
|
||||
public Long getLogTime() {
|
||||
return logTime;
|
||||
}
|
||||
|
||||
public void setLogTime(Long logTime) {
|
||||
this.logTime = logTime;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.base.util.JsonUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 封装登录信息至JWT得到token
|
||||
*
|
||||
* @author: maxf
|
||||
* @date: 2018/5/31 21:34
|
||||
*/
|
||||
public class LoginToken implements Serializable {
|
||||
/**
|
||||
* 消费者用户名(手机号)
|
||||
*/
|
||||
private String username;
|
||||
|
||||
public LoginToken() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonUtil.obj2Json(this);
|
||||
}
|
||||
|
||||
public LoginToken(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.springboot.base.autoconfigure.MutiRedisAutoConfiguration;
|
||||
import com.yexuejc.springboot.base.security.inte.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Security Web 配置
|
||||
*
|
||||
* @ClassName: SecurityConfig
|
||||
* @Description:Security Web 配置
|
||||
* @author: maxf
|
||||
* @date: 2017年11月22日 下午4:40:22
|
||||
*/
|
||||
//@EnableWebSecurity(debug = false)
|
||||
public abstract class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier(MutiRedisAutoConfiguration.BEAN_REDIS_TEMPLATE0)
|
||||
private RedisTemplate<Object, Object> redisTemplate0;
|
||||
|
||||
|
||||
@Bean
|
||||
public static NoOpPasswordEncoder passwordEncoder() {
|
||||
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据库DB=>获取用户数据loadUserByUsername()
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract UserService getUserService();
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
UserDetailsService userDetailsService = new UserDetailsManager(getUserService());
|
||||
auth.authenticationProvider(new ConsumerAuthenticationProvider(userDetailsService, getUserService()));
|
||||
auth.userDetailsService(userDetailsService);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public ConsumerAuthenticationProcessingFilter consumerAuthenticationProcessingFilter(
|
||||
AuthenticationManager authenticationManager) throws Exception {
|
||||
ConsumerAuthenticationProcessingFilter filter = new ConsumerAuthenticationProcessingFilter
|
||||
(authenticationManager);
|
||||
filter.setAuthenticationManager(this.authenticationManager());
|
||||
loginHodler(filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 处理登录
|
||||
* 成功: filter.setAuthenticationSuccessHandler()
|
||||
* 失败: filter.setAuthenticationFailureHandler()
|
||||
* </pre>
|
||||
*
|
||||
* @param filter
|
||||
*/
|
||||
protected abstract void loginHodler(ConsumerAuthenticationProcessingFilter filter);
|
||||
|
||||
|
||||
@Bean
|
||||
public LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint() {
|
||||
LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint
|
||||
("/login");
|
||||
return loginUrlAuthenticationEntryPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解决跨域问题
|
||||
* <pre>
|
||||
* http.csrf().disable()
|
||||
* .cors()
|
||||
* .and()
|
||||
* </pre>
|
||||
* 参考 https://www.jianshu.com/p/87e1ef68794c -> https://github.com/gothinkster/spring-boot-realworld-example-app
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
final CorsConfiguration configuration = new CorsConfiguration();
|
||||
//指定允许跨域的请求(*所有):http://wap.guansichou.com
|
||||
configuration.setAllowedOrigins(Arrays.asList("*"));
|
||||
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
|
||||
// setAllowCredentials(true) is important, otherwise:
|
||||
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode
|
||||
// is 'include'.
|
||||
configuration.setAllowCredentials(true);
|
||||
// setAllowedHeaders is important! Without it, OPTIONS preflight request
|
||||
// will fail with 403 Invalid CORS request
|
||||
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "X-User-Agent", "Content-Type"));
|
||||
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关键.cors()
|
||||
*
|
||||
* @param http
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.cors()
|
||||
.and().servletApi().disable()
|
||||
.requestCache().disable()
|
||||
.securityContext().securityContextRepository(new ConsumerSecurityContextRepository(redisTemplate0))
|
||||
.and()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
|
||||
http.addFilterAt(consumerAuthenticationProcessingFilter(super.authenticationManager()),
|
||||
UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
// 不需要经过SpringSecurity的过滤器的URLs
|
||||
// web.ignoring();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
import com.yexuejc.springboot.base.security.inte.User;
|
||||
import com.yexuejc.springboot.base.security.inte.UserService;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ConsumerJdbcDaoImpl
|
||||
*
|
||||
* @ClassName: ConsumerJdbcDaoImpl
|
||||
* @Description:ConsumerJdbcDaoImpl
|
||||
* @author: maxf
|
||||
* @date: 2017年12月21日 下午6:17:12
|
||||
*/
|
||||
public class UserDetailsManager extends InMemoryUserDetailsManager {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public UserDetailsManager(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
User consumer = userService.getConsumerByUserName(username);
|
||||
if (StrUtil.isEmpty(consumer)) {
|
||||
throw new UsernameNotFoundException(username);
|
||||
}
|
||||
// 处理用户权限
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
for (String role : consumer.getRoles()) {
|
||||
authorities.add(new SimpleGrantedAuthority(role));
|
||||
}
|
||||
ConsumerUser consumerUser = new ConsumerUser(consumer.getMobile(), consumer.getPwd(),
|
||||
consumer.getEnable(), consumer.getNonExpire(), true, consumer.getNonLock(),
|
||||
authorities, consumer.getConsumerId(), null, System.currentTimeMillis());
|
||||
return consumerUser;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.yexuejc.springboot.base.security.inte;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author maxf
|
||||
* @version 1.0
|
||||
* @ClassName User
|
||||
* @Description
|
||||
* @date 2018/11/8 17:19
|
||||
*/
|
||||
public interface User {
|
||||
/**
|
||||
* 角色
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<String> getRoles();
|
||||
|
||||
|
||||
String getMobile();
|
||||
|
||||
String getPwd();
|
||||
|
||||
boolean getEnable();
|
||||
|
||||
boolean getNonExpire();
|
||||
|
||||
boolean getNonLock();
|
||||
|
||||
String getConsumerId();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.yexuejc.springboot.base.security.inte;
|
||||
|
||||
import com.yexuejc.base.pojo.ApiVO;
|
||||
import com.yexuejc.springboot.base.constant.BizConsts;
|
||||
import com.yexuejc.springboot.base.security.ConsumerToken;
|
||||
|
||||
/**
|
||||
* @author maxf
|
||||
* @version 1.0
|
||||
* @ClassName UserService
|
||||
* @Description
|
||||
* @date 2018/11/8 17:17
|
||||
*/
|
||||
public interface UserService {
|
||||
/**
|
||||
* 根据用户名到数据库查询用户
|
||||
*
|
||||
* @param username 登录账号
|
||||
* @return
|
||||
*/
|
||||
User getConsumerByUserName(String username);
|
||||
|
||||
/**
|
||||
* 校验短信验证码=>短信登录
|
||||
*
|
||||
* @param redisBiz {@link BizConsts.CONSUMER_LOGIN_SMS} 业务id reids使用
|
||||
* @param username 登录账号
|
||||
* @param smscode 短信验证码
|
||||
* @return
|
||||
*/
|
||||
ApiVO checkSmsCode2Redis(String redisBiz, String username, String smscode);
|
||||
|
||||
/**
|
||||
* 校验第三方登录openid
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @return apiVO.setObject1(User.class) 自己封装登录用户信息
|
||||
*/
|
||||
ApiVO checkOpenId(ConsumerToken consumerToken);
|
||||
|
||||
/**
|
||||
* 没有账号时根据登录信息创建账号
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @return
|
||||
*/
|
||||
ApiVO addConsumer(ConsumerToken consumerToken);
|
||||
}
|
|
@ -2,8 +2,10 @@ package com.yexuejc.springboot.base;
|
|||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
|
||||
|
||||
@SpringBootApplication
|
||||
@SpringBootApplication(exclude = {RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})
|
||||
public class ApplicationRun {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ApplicationRun.class, args);
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package com.yexuejc.springboot.base.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.yexuejc.springboot.base.security.domain.Consumer;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author yexuejc
|
||||
* @since 2018-05-27
|
||||
*/
|
||||
@Mapper
|
||||
public interface ConsumerMapper extends BaseMapper<Consumer> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 插入一条记录
|
||||
* </p>
|
||||
*
|
||||
* @param entity 实体对象
|
||||
* @return int
|
||||
*/
|
||||
@Override
|
||||
int insert(Consumer entity);
|
||||
|
||||
|
||||
/**
|
||||
* 修改权限
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
Integer updateRoles(@Param(Constants.ENTITY) Consumer entity);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.yexuejc.springboot.base.mapper.handler;
|
||||
|
||||
import com.yexuejc.base.util.JsonUtil;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* 数据库查询转换 -> JSON
|
||||
*/
|
||||
@MappedTypes({Object.class})
|
||||
public class JsonTypeHandler extends BaseTypeHandler<Object> {
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
|
||||
throws SQLException {
|
||||
if (parameter == null) {
|
||||
ps.setString(i, null);
|
||||
} else {
|
||||
ps.setString(i, JsonUtil.obj2Json(parameter));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
if (rs.getString(columnName) == null) {
|
||||
return null;
|
||||
}
|
||||
return JsonUtil.json2Obj(rs.getString(columnName), Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
if (rs.getString(columnIndex) == null) {
|
||||
return null;
|
||||
}
|
||||
return JsonUtil.json2Obj(rs.getString(columnIndex), Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
if (cs.getString(columnIndex) == null) {
|
||||
return null;
|
||||
}
|
||||
return JsonUtil.json2Obj(cs.getString(columnIndex), Object.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.yexuejc.base.constant.RespsConsts;
|
||||
import com.yexuejc.base.http.Resps;
|
||||
import com.yexuejc.base.util.JsonUtil;
|
||||
import com.yexuejc.base.util.JwtUtil;
|
||||
import com.yexuejc.base.util.RegexUtil;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
import com.yexuejc.springboot.base.autoconfigure.MutiRedisAutoConfiguration;
|
||||
import com.yexuejc.springboot.base.constant.BizConsts;
|
||||
import com.yexuejc.springboot.base.exception.ThirdPartyAuthorizationException;
|
||||
import com.yexuejc.springboot.base.security.inte.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author maxf
|
||||
* @version 1.0
|
||||
* @ClassName SecurityConfig
|
||||
* @Description
|
||||
* @date 2018/11/8 17:30
|
||||
*/
|
||||
@EnableWebSecurity(debug = false)
|
||||
public class MySecurityConfig extends SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("userserviceimpl")
|
||||
UserService userService;
|
||||
|
||||
@Override
|
||||
protected UserService getUserService() {
|
||||
return userService;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier(MutiRedisAutoConfiguration.BEAN_REDIS_TEMPLATE0)
|
||||
private RedisTemplate<Object, Object> redisTemplate0;
|
||||
|
||||
/**
|
||||
* 保存登录信息至redis
|
||||
*
|
||||
* @param redisTemplate0 redis 链接
|
||||
* @param user 登录用户
|
||||
* @param roles 角色信息
|
||||
* @param token 登录token
|
||||
* @param isPast 是否设置过期
|
||||
*/
|
||||
private void saveLoginUser(RedisTemplate<Object, Object> redisTemplate0, ConsumerUser user, List<String> roles, String token,
|
||||
boolean isPast) {
|
||||
Map<String, Object> m = new HashMap<>(4);
|
||||
m.put("username", user.getUsername());
|
||||
m.put("token", token);
|
||||
m.put("roles", roles);
|
||||
m.put("id", user.getId());
|
||||
m.put("logType", user.getLogType());
|
||||
m.put("logTime", user.getLogTime());
|
||||
redisTemplate0.opsForHash().putAll(BizConsts.CONSUMER_LOGIN_REDIS + "." + user.getUsername(), m);
|
||||
if (isPast) {
|
||||
//对于没有绑定手机号的token,10分钟后过期
|
||||
redisTemplate0.expire(BizConsts.CONSUMER_LOGIN_REDIS + "." + user.getUsername(), 10, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 处理登录
|
||||
* 成功: filter.setAuthenticationSuccessHandler()
|
||||
* 失败: filter.setAuthenticationFailureHandler()
|
||||
* </pre>
|
||||
*
|
||||
* @param filter
|
||||
*/
|
||||
@Override
|
||||
protected void loginHodler(ConsumerAuthenticationProcessingFilter filter) {
|
||||
filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
|
||||
String token = JwtUtil.instace().compact(new LoginToken(authentication.getName()));
|
||||
ConsumerUser user = (ConsumerUser) authentication.getPrincipal();
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
List<String> roles = new ArrayList<>();
|
||||
if (authorities != null && authorities.size() > 0) {
|
||||
for (GrantedAuthority g : authorities) {
|
||||
roles.add(g.getAuthority());
|
||||
}
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>(2);
|
||||
map.put("token", token);
|
||||
map.put("bindMobile", false);
|
||||
if (StrUtil.isEmpty(user.getUsername()) || !RegexUtil.regex(user.getUsername(), RegexUtil.REGEX_MOBILE)) {
|
||||
map.put("bindMobile", true);
|
||||
}
|
||||
saveLoginUser(redisTemplate0, user, roles, token, (Boolean) map.get("bindMobile"));
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write(JsonUtil.obj2Json(Resps.success().setSucc(map)));
|
||||
response.getWriter().close();
|
||||
});
|
||||
filter.setAuthenticationFailureHandler((request, response, exception) -> {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
Resps resps = new Resps();
|
||||
if (exception instanceof DisabledException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_IS_LOCK_MSG});
|
||||
} else if (exception instanceof AccountExpiredException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_IS_EXPIRE_MSG});
|
||||
} else if (exception instanceof CredentialsExpiredException) {
|
||||
resps.setErr(BizConsts.BASE_LOGIN_IS_EXPIRE_CODE, new String[]{BizConsts.BASE_LOGIN_IS_EXPIRE_MSG});
|
||||
} else if (exception instanceof LockedException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_IS_LOCKED_MSG});
|
||||
} else if (exception instanceof AuthenticationCredentialsNotFoundException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_CREDENTIALS_NOT_FOUND_MSG});
|
||||
} else if (exception instanceof ThirdPartyAuthorizationException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{exception.getMessage()});
|
||||
} else if (exception instanceof BadCredentialsException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_PWD_IS_ERR_MSG});
|
||||
} else if (exception instanceof UsernameNotFoundException) {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_ACCOUNT_NOT_FOUND_MSG});
|
||||
} else {
|
||||
resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_SYS_ERR_MSG});
|
||||
}
|
||||
response.getWriter().write(JsonUtil.obj2Json(resps));
|
||||
response.getWriter().close();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
super.configure(http);
|
||||
http.headers().frameOptions().disable();
|
||||
/**
|
||||
* 权限控制
|
||||
* ->无权限
|
||||
*/
|
||||
http.authorizeRequests().antMatchers(
|
||||
"/", "/index"
|
||||
).permitAll();
|
||||
|
||||
/**
|
||||
* 权限控制
|
||||
* ->登录可访问
|
||||
*/
|
||||
http.authorizeRequests().antMatchers(
|
||||
"/consumer/**", "/sms/bind/**").authenticated();
|
||||
|
||||
// 登出处理。
|
||||
http.logout().logoutSuccessHandler((request, response, authentication) -> {
|
||||
redisTemplate0.delete(BizConsts.CONSUMER_LOGIN_REDIS + "." + authentication.getName());
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write(JsonUtil.obj2Json(Resps.success()));
|
||||
response.getWriter().close();
|
||||
});
|
||||
|
||||
// 未登录,却访问需要登录的接口时的处理
|
||||
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setStatus(401);
|
||||
response.getWriter().write(
|
||||
JsonUtil.obj2Json(
|
||||
Resps.error(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_NOT_LOGIN_MSG})
|
||||
)
|
||||
);
|
||||
response.getWriter().close();
|
||||
});
|
||||
// 已登录,但当前用户没有访问的某个接口的权限时的处理
|
||||
http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setStatus(401);
|
||||
response.getWriter().write(
|
||||
JsonUtil.obj2Json(
|
||||
Resps.error(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_NOT_ROLE_MSG})
|
||||
)
|
||||
);
|
||||
response.getWriter().close();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
package com.yexuejc.springboot.base.security;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.yexuejc.base.constant.RespsConsts;
|
||||
import com.yexuejc.base.pojo.ApiVO;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
import com.yexuejc.springboot.base.autoconfigure.MutiRedisAutoConfiguration;
|
||||
import com.yexuejc.springboot.base.constant.BizConsts;
|
||||
import com.yexuejc.springboot.base.constant.DictRegTypeConsts;
|
||||
import com.yexuejc.springboot.base.constant.LogTypeConsts;
|
||||
import com.yexuejc.springboot.base.exception.ThirdPartyAuthorizationException;
|
||||
import com.yexuejc.springboot.base.mapper.ConsumerMapper;
|
||||
import com.yexuejc.springboot.base.security.domain.Consumer;
|
||||
import com.yexuejc.springboot.base.security.inte.User;
|
||||
import com.yexuejc.springboot.base.security.inte.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库操作DB
|
||||
*
|
||||
* @author maxf
|
||||
* @version 1.0
|
||||
* @ClassName UserServiceImpl
|
||||
* @Description
|
||||
* @date 2018/11/8 17:31
|
||||
*/
|
||||
@Service("userserviceimpl")
|
||||
public class UserServiceImpl implements UserService {
|
||||
@Autowired
|
||||
ConsumerMapper consumerMapper;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(MutiRedisAutoConfiguration.BEAN_REDIS_TEMPLATE1)
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* 根据用户名到数据库查询用户
|
||||
*
|
||||
* @param username 登录账号
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public User getConsumerByUserName(String username) {
|
||||
QueryWrapper queryWrapper = new QueryWrapper();
|
||||
queryWrapper.eq("mobile", username);
|
||||
Consumer consumer = consumerMapper.selectOne(queryWrapper);
|
||||
return consumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 到自己的reids里面校验短信验证码
|
||||
*
|
||||
* @param smsType {@linkplain BizConsts.CONSUMER_LOGIN_SMS} 业务id reids使用
|
||||
* @param mobile 登录账号
|
||||
* @param code 短信验证码
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ApiVO checkSmsCode2Redis(String smsType, String mobile, String code) {
|
||||
redisTemplate.afterPropertiesSet();
|
||||
String rCode = (String) redisTemplate.opsForHash().get(smsType + "." + mobile, "code");
|
||||
Integer validatedNums = (Integer) redisTemplate.opsForHash().get(smsType + "." + mobile, "validatedNums");
|
||||
if (validatedNums == null) {
|
||||
return new ApiVO(ApiVO.STATUS.F, RespsConsts.CODE_FAIL, "验证码过期,请重新获取");
|
||||
} else if (validatedNums > 5) {
|
||||
redisTemplate.delete(smsType + "." + mobile);
|
||||
return new ApiVO(ApiVO.STATUS.F, RespsConsts.CODE_FAIL, "验证码过期,请重新获取");
|
||||
}
|
||||
if (code.equals(rCode)) {
|
||||
redisTemplate.delete(smsType + "." + mobile);
|
||||
return new ApiVO(ApiVO.STATUS.S);
|
||||
} else {
|
||||
validatedNums++;
|
||||
redisTemplate.opsForHash().put(smsType + "." + mobile, "validatedNums", validatedNums);
|
||||
return new ApiVO(ApiVO.STATUS.F, RespsConsts.CODE_FAIL, "验证码不正确");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验openid 根据自己业务做判断
|
||||
* <br/>
|
||||
* 返回:封装登录用户信息到 apiVO.setObject1(User.class) 自己封装登录用户信息
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ApiVO checkOpenId(ConsumerToken consumerToken) {
|
||||
ApiVO apiVO = new ApiVO(ApiVO.STATUS.F, "没有找到用户信息");
|
||||
switch (consumerToken.getLogtype()) {
|
||||
case LogTypeConsts.QQ:
|
||||
apiVO = checkOpenid4QQ(consumerToken, true);
|
||||
break;
|
||||
case LogTypeConsts.WECHAT:
|
||||
apiVO = checkOpenid4Wechat(consumerToken, true);
|
||||
break;
|
||||
case LogTypeConsts.WEIBO:
|
||||
apiVO = checkOpenid4Weibo(consumerToken, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return apiVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录 QQ登录
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public ApiVO checkOpenid4QQ(ConsumerToken consumerToken, boolean b) {
|
||||
Consumer consumer = getConsumerByQQOpenid(consumerToken.getOpenid());
|
||||
if (consumer == null) {
|
||||
return new ApiVO(ApiVO.STATUS.F, "获取用户信息失败");
|
||||
}
|
||||
if (b && DictRegTypeConsts.DICT_QQ.equals(consumer.getRegType())) {
|
||||
//如果是qq注册的,登录的同时更新用户信息
|
||||
updateConsumer(consumer, consumerToken);
|
||||
}
|
||||
return new ApiVO(ApiVO.STATUS.S).setObject1(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录 微信登录
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public ApiVO checkOpenid4Wechat(ConsumerToken consumerToken, boolean b) {
|
||||
Consumer consumer = getConsumerByWechatOpenid(consumerToken.getOpenid());
|
||||
if (consumer == null) {
|
||||
return new ApiVO(ApiVO.STATUS.F, "获取用户信息失败");
|
||||
}
|
||||
if (b && DictRegTypeConsts.DICT_WECHAT.equals(consumer.getRegType())) {
|
||||
//如果是微信注册的,登录的同时更新用户信息
|
||||
updateConsumer(consumer, consumerToken);
|
||||
}
|
||||
return new ApiVO(ApiVO.STATUS.S).setObject1(consumer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 第三方登录 微博登录
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
public ApiVO checkOpenid4Weibo(ConsumerToken consumerToken, boolean b) {
|
||||
Consumer consumer = getConsumerByWeiboOpenid(consumerToken.getOpenid());
|
||||
if (consumer == null) {
|
||||
return new ApiVO(ApiVO.STATUS.F, "获取用户信息失败");
|
||||
}
|
||||
if (b && DictRegTypeConsts.DICT_WEIBO.equals(consumer.getRegType())) {
|
||||
//如果是微博注册的,登录的同时更新用户信息
|
||||
updateConsumer(consumer, consumerToken);
|
||||
}
|
||||
return new ApiVO(ApiVO.STATUS.S).setObject1(consumer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 没有账号时处理自己的业务,此次必须返回 构造出的登录用户,否则会抛出{@link ThirdPartyAuthorizationException 第三方授权异常}
|
||||
* <br/>
|
||||
* 返回:封装登录用户信息到 apiVO.setObject1(User.class) 自己封装登录用户信息
|
||||
*
|
||||
* @param consumerToken 登录信息
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ApiVO addConsumer(ConsumerToken consumerToken) {
|
||||
Consumer consumer = new Consumer();
|
||||
consumer.setConsumerId(StrUtil.genUUID());
|
||||
consumer.setMobile(StrUtil.isNotEmpty(consumerToken.getUsername()) ? consumerToken.getUsername() : consumerToken.getOpenid());
|
||||
consumer.setPwd(StrUtil.toMD5("123456"));
|
||||
consumer.setEnable(true);
|
||||
consumer.setNonExpire(true);
|
||||
consumer.setNonLock(true);
|
||||
List<String> roles = new ArrayList<>();
|
||||
roles.add("ROLE_CONSUMER");
|
||||
consumer.setRoles(roles);
|
||||
switch (consumerToken.getLogtype()) {
|
||||
case LogTypeConsts.SMS:
|
||||
ApiVO apiVO = checkSmsCode2Redis(BizConsts.CONSUMER_LOGIN_SMS, consumerToken.getUsername(),
|
||||
consumerToken.getSmscode());
|
||||
if (apiVO.isFail()) {
|
||||
return apiVO;
|
||||
}
|
||||
consumer.setNickname(consumerToken.getUsername());
|
||||
consumer.setHead("/head/def.png");
|
||||
consumer.setRegType(DictRegTypeConsts.DICT_MOBILE);
|
||||
break;
|
||||
case LogTypeConsts.QQ:
|
||||
consumer.setQqId(consumerToken.getOpenid());
|
||||
consumer.setNickname(consumerToken.getNickname());
|
||||
setHeader(consumerToken, consumer, false);
|
||||
setSex(consumerToken, consumer);
|
||||
consumer.setRegType(DictRegTypeConsts.DICT_QQ);
|
||||
break;
|
||||
case LogTypeConsts.WECHAT:
|
||||
consumer.setWechatId(consumerToken.getOpenid());
|
||||
consumer.setNickname(consumerToken.getNickname());
|
||||
setHeader(consumerToken, consumer, false);
|
||||
setSex(consumerToken, consumer);
|
||||
consumer.setRegType(DictRegTypeConsts.DICT_WECHAT);
|
||||
break;
|
||||
case LogTypeConsts.WEIBO:
|
||||
consumer.setWeiboId(consumerToken.getOpenid());
|
||||
consumer.setNickname(consumerToken.getNickname());
|
||||
setHeader(consumerToken, consumer, false);
|
||||
setSex(consumerToken, consumer);
|
||||
consumer.setRegType(DictRegTypeConsts.DICT_WEIBO);
|
||||
break;
|
||||
default:
|
||||
return new ApiVO(ApiVO.STATUS.F, "暂不支持的登录方式");
|
||||
}
|
||||
Integer result = consumerMapper.insert(consumer);
|
||||
if (result < 1) {
|
||||
return new ApiVO(ApiVO.STATUS.F, RespsConsts.CODE_FAIL, "登录失败");
|
||||
}
|
||||
return new ApiVO(ApiVO.STATUS.S).setObject1(consumer);
|
||||
}
|
||||
|
||||
|
||||
public Consumer getConsumerByQQOpenid(String openid) {
|
||||
QueryWrapper queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("qq_id", openid);
|
||||
Consumer consumer = consumerMapper.selectOne(queryWrapper);
|
||||
return consumer;
|
||||
}
|
||||
|
||||
public Consumer getConsumerByWechatOpenid(String openid) {
|
||||
QueryWrapper queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("wechat_id", openid);
|
||||
Consumer consumer = consumerMapper.selectOne(queryWrapper);
|
||||
return consumer;
|
||||
}
|
||||
|
||||
public Consumer getConsumerByWeiboOpenid(String openid) {
|
||||
QueryWrapper queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("weibo_id", openid);
|
||||
Consumer consumer = consumerMapper.selectOne(queryWrapper);
|
||||
return consumer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新基本信息
|
||||
*
|
||||
* @param consumer
|
||||
* @param consumerToken
|
||||
*/
|
||||
private void updateConsumer(Consumer consumer, ConsumerToken consumerToken) {
|
||||
boolean b1, b2, b3;
|
||||
b1 = b2 = b3 = true;
|
||||
if (StrUtil.isNotEmpty(consumerToken.getNickname())) {
|
||||
if (consumerToken.getNickname().equals(consumer.getNickname())) {
|
||||
b1 = false;
|
||||
}
|
||||
consumer.setNickname(consumerToken.getNickname());
|
||||
}
|
||||
b2 = setHeader(consumerToken, consumer, true);
|
||||
b3 = setSex(consumerToken, consumer);
|
||||
if (!b1 && !b2 && !b3) {
|
||||
return;
|
||||
}
|
||||
LambdaUpdateWrapper<Consumer> queryWrapper = new UpdateWrapper<>(new Consumer()).lambda();
|
||||
try {
|
||||
queryWrapper.set(Consumer::getNickname, consumer.getNickname())
|
||||
.set(Consumer::getHead, consumer.getHead())
|
||||
.set(Consumer::getSex, consumer.getSex())
|
||||
.eq(Consumer::getConsumerId, consumer.getConsumerId());
|
||||
consumerMapper.update(new Consumer(), queryWrapper);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置头像->上传网络图片到OSS
|
||||
*
|
||||
* @param consumerToken
|
||||
* @param consumer
|
||||
* @param isUpdate
|
||||
*/
|
||||
private boolean setHeader(ConsumerToken consumerToken, Consumer consumer, boolean isUpdate) {
|
||||
if (StrUtil.isNotEmpty(consumerToken.getHead())) {
|
||||
if (isUpdate) {
|
||||
if (consumerToken.getHead().equals(consumer.getSourceHead())) {
|
||||
//未改变头像
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
consumer.setSourceHead(consumerToken.getHead());
|
||||
}
|
||||
//应该上传至OSS后返回OSS地址
|
||||
consumer.setHead(consumerToken.getHead());
|
||||
// try {
|
||||
// consumer.setHead(putOss4Head(null, consumerToken.getHead()));
|
||||
// } catch (ImageException e) {
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置性别
|
||||
*
|
||||
* @param consumerToken
|
||||
* @param consumer
|
||||
*/
|
||||
private boolean setSex(ConsumerToken consumerToken, Consumer consumer) {
|
||||
if (StrUtil.isNotEmpty(consumerToken.getSex())) {
|
||||
if ("1".equals(consumerToken.getSex()) && !"男".equals(consumer.getSex())) {
|
||||
consumer.setSex("男");
|
||||
return true;
|
||||
} else if ("2".equals(consumerToken.getSex()) && !"女".equals(consumer.getSex())) {
|
||||
consumer.setSex("女");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
package com.yexuejc.springboot.base.security.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.yexuejc.base.util.JsonUtil;
|
||||
import com.yexuejc.springboot.base.security.inte.User;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @author yexuejc
|
||||
* @since 2018-05-27
|
||||
*/
|
||||
@TableName(resultMap = "BaseResultMap")
|
||||
public class Consumer extends Model<Consumer> implements User {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@TableId(value = "consumer_id", type = IdType.UUID)
|
||||
private String consumerId;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@TableField("mobile")
|
||||
private String mobile;
|
||||
/**
|
||||
* 密码:md5
|
||||
*/
|
||||
@TableField("pwd")
|
||||
private String pwd;
|
||||
/**
|
||||
* 账号是否启用
|
||||
*/
|
||||
@TableField("is_enable")
|
||||
private boolean enable;
|
||||
/**
|
||||
* 账号是否没有过期
|
||||
*/
|
||||
@TableField("is_non_expire")
|
||||
private boolean nonExpire;
|
||||
/**
|
||||
* 账号是否没有被锁定
|
||||
*/
|
||||
@TableField("is_non_lock")
|
||||
private boolean nonLock;
|
||||
/**
|
||||
* 微信id
|
||||
*/
|
||||
@TableField("wechat_id")
|
||||
private String wechatId;
|
||||
/**
|
||||
* qq id
|
||||
*/
|
||||
@TableField("qq_id")
|
||||
private String qqId;
|
||||
/**
|
||||
* 微博id
|
||||
*/
|
||||
@TableField("weibo_id")
|
||||
private String weiboId;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@TableField("nickname")
|
||||
private String nickname;
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
@TableField("head")
|
||||
private String head;
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
@TableField("email")
|
||||
private String email;
|
||||
/**
|
||||
* 用户姓别 '男',‘女’
|
||||
*/
|
||||
@TableField("sex")
|
||||
private String sex;
|
||||
/**
|
||||
* 角色、权限
|
||||
*/
|
||||
@TableField(value = "roles", el = "roles,typeHandler=com.yexuejc.guansc.core.mybatis.handler.JsonTypeHandler")
|
||||
private List<String> roles;
|
||||
/**
|
||||
* 支付密码
|
||||
*/
|
||||
@TableField("pay_pwd")
|
||||
private String payPwd;
|
||||
/**
|
||||
* 注册方式
|
||||
*/
|
||||
@TableField("reg_type")
|
||||
private String regType;
|
||||
/**
|
||||
* 第三方源头像路径
|
||||
*/
|
||||
@TableField("source_head")
|
||||
private String sourceHead;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonUtil.obj2Json(this);
|
||||
}
|
||||
|
||||
public String getConsumerId() {
|
||||
return consumerId;
|
||||
}
|
||||
|
||||
public Consumer setConsumerId(String consumerId) {
|
||||
this.consumerId = consumerId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public Consumer setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPwd() {
|
||||
return pwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnable() {
|
||||
return this.enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNonExpire() {
|
||||
return this.nonExpire;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNonLock() {
|
||||
return this.nonLock;
|
||||
}
|
||||
|
||||
public Consumer setPwd(String pwd) {
|
||||
this.pwd = pwd;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Consumer setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Consumer setNonExpire(boolean nonExpire) {
|
||||
this.nonExpire = nonExpire;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Consumer setNonLock(boolean nonLock) {
|
||||
this.nonLock = nonLock;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getWechatId() {
|
||||
return wechatId;
|
||||
}
|
||||
|
||||
public Consumer setWechatId(String wechatId) {
|
||||
this.wechatId = wechatId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getQqId() {
|
||||
return qqId;
|
||||
}
|
||||
|
||||
public Consumer setQqId(String qqId) {
|
||||
this.qqId = qqId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getWeiboId() {
|
||||
return weiboId;
|
||||
}
|
||||
|
||||
public Consumer setWeiboId(String weiboId) {
|
||||
this.weiboId = weiboId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public Consumer setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getHead() {
|
||||
return head;
|
||||
}
|
||||
|
||||
public Consumer setHead(String head) {
|
||||
this.head = head;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public Consumer setEmail(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public Consumer setSex(String sex) {
|
||||
this.sex = sex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public Consumer setRoles(List<String> roles) {
|
||||
this.roles = roles;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPayPwd() {
|
||||
return payPwd;
|
||||
}
|
||||
|
||||
public Consumer setPayPwd(String payPwd) {
|
||||
this.payPwd = payPwd;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRegType() {
|
||||
return regType;
|
||||
}
|
||||
|
||||
public Consumer setRegType(String regType) {
|
||||
this.regType = regType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSourceHead() {
|
||||
return sourceHead;
|
||||
}
|
||||
|
||||
public Consumer setSourceHead(String sourceHead) {
|
||||
this.sourceHead = sourceHead;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Serializable pkVal() {
|
||||
return this.consumerId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.yexuejc.springboot.base.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Security 登录注册相关controller
|
||||
* 主要实现
|
||||
* 1.短信登录发送验证码
|
||||
* 2.第三方登录绑定手机号(以及绑定手机号发送验证码)
|
||||
* </pre>
|
||||
*
|
||||
* @author maxf
|
||||
* @version 1.0
|
||||
* @ClassName SecurityCtrl
|
||||
* @Description
|
||||
* @date 2018/11/9 10:52
|
||||
*/
|
||||
@RestController
|
||||
public class SecurityCtrl {
|
||||
|
||||
}
|
|
@ -6,36 +6,31 @@ logging.level.root=info
|
|||
logging.path=/logs/yexuejc-springboot-parent
|
||||
|
||||
|
||||
yexuejc.http.filter.type=0
|
||||
yexuejc.http.filter.type=1
|
||||
|
||||
|
||||
yexuejc.http.encrypt.encrypt=true
|
||||
yexuejc.http.encrypt.decrypt=true
|
||||
#配置密钥方式
|
||||
#配置密钥方式
|
||||
#yexuejc.http.encrypt.private-key=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAiSo5blJ9-QJ0_QElcy5AaRTq-3oO4lJ8PvIOIt-Xr5SUFODVj3DUbiy6_0bxQYO3NiYHlXPb37UVV3jjlXJsXwIDAQABAkBE0WOJH2hGs93gRl_0vwLf9ffDfkTTdlER_73p70aad3QZRslEkinQH7G5aE_DgBm5m72TCeH-PD2FZ2lwtavBAiEAvnRown5Lpqbl0tN_OUxr_e1u9d_-8dNL_JEETO7BZCECIQC4XtY-18j0bVVLxaXPjKQ00D59yntwObihDNyRK0nAfwIgHPHEGgrnpGQo-Wl7JFIg925mNqfcLxRVsAS6CpcefQECIQCUsLdsmy6QIhTmNRJSXoSXq1KatE_05DhIekzwLs8eFQIgfMawMiu52ZxBI5_pZ7ancQZ6Dsxl45utFqJShzV1pio
|
||||
#yexuejc.http.encrypt.public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIkqOW5SffkCdP0BJXMuQGkU6vt6DuJSfD7yDiLfl6-UlBTg1Y9w1G4suv9G8UGDtzYmB5Vz29-1FVd445VybF8CAwEAAQ
|
||||
#配置证书方式
|
||||
#配置证书方式
|
||||
yexuejc.http.encrypt.private-key-path=/lgfishing.keystore
|
||||
yexuejc.http.encrypt.private-alias=lgfishing
|
||||
yexuejc.http.encrypt.private-pwd=lgfishing2018
|
||||
|
||||
|
||||
#编码
|
||||
#编码
|
||||
spring.http.encoding.force=true
|
||||
spring.http.encoding.charset=UTF-8
|
||||
spring.http.encoding.enabled=true
|
||||
server.tomcat.uri-encoding=UTF-8
|
||||
|
||||
#是否开启HTTPS(SSL)请求证书验证忽略:默认false
|
||||
#是否开启HTTPS(SSL)请求证书验证忽略:默认false
|
||||
yexuejc.enable.ssl-ignore=true
|
||||
|
||||
#reids
|
||||
#开启指定redis库db0默认开启
|
||||
yexuejc.redis.db1=true
|
||||
yexuejc.redis.db2=true
|
||||
spring.redis.host=121.42.165.89
|
||||
spring.redis.password=
|
||||
spring.redis.port=16379
|
||||
|
||||
|
||||
|
||||
|
||||
#mns
|
||||
|
@ -49,3 +44,50 @@ yexuejc.alibaba.oss.endpoint=oss-cn-beijing.aliyuncs.com
|
|||
yexuejc.alibaba.oss.access-key-secret=
|
||||
yexuejc.alibaba.oss.access-key-id=
|
||||
yexuejc.alibaba.oss.bucket=guansichou
|
||||
|
||||
|
||||
|
||||
#========================================================================================================================
|
||||
# security相关
|
||||
#reids
|
||||
#开启指定redis库db0默认开启
|
||||
yexuejc.redis.db1=true
|
||||
spring.redis.jedis.pool.max-active=100
|
||||
spring.redis.jedis.pool.max-idle=10
|
||||
spring.redis.jedis.pool.min-idle=3
|
||||
spring.redis.host=121.42.165.89
|
||||
spring.redis.password=
|
||||
spring.redis.port=16379
|
||||
|
||||
|
||||
#db
|
||||
|
||||
spring.h2.console.path=/h2-console
|
||||
spring.h2.console.enabled=true
|
||||
spring.h2.console.settings.web-allow-others=true
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=123456
|
||||
spring.datasource.url=jdbc:h2:mem:test;MODE=PostgreSQL
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.schema=classpath:db/schema.sql
|
||||
spring.datasource.data=classpath:db/data.sql
|
||||
|
||||
#========================================================================================================================
|
||||
#mybatis-plus
|
||||
mybatis-plus.mapper-locations=classpath*:mapper/*.xml
|
||||
#实体扫描,多个package用逗号或者分号分隔
|
||||
mybatis-plus.type-aliases-package=com.yexuejc.springboot.base.security.domain
|
||||
#主键类型0:"数据库ID自增", 1:"用户输入ID",2:"该类型为未设置主键类型", 3:"全局唯一ID UUID",4:全局唯一ID (UUID),5:字符串全局唯一ID (idWorker 的字符串表示);
|
||||
mybatis-plus.global-config.db-config.id-type=uuid
|
||||
mybatis-plus.global-config.db-config.db-type=POSTGRE_SQL
|
||||
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
|
||||
mybatis-plus.global-config.db-config.field-strategy=not_empty
|
||||
#驼峰下划线转换
|
||||
mybatis-plus.global-config.db-config.column-underline=true
|
||||
#逻辑删除配置(下面3个配置)
|
||||
mybatis-plus.global-config.db-config.logic-delete-value=true
|
||||
mybatis-plus.global-config.db-config.logic-not-delete-value=false
|
||||
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
|
||||
mybatis-plus.configuration.map-underscore-to-camel-case=true
|
||||
mybatis-plus.configuration.cache-enabled=false
|
||||
#========================================================================================================================
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
INSERT INTO consumer (consumer_id,
|
||||
mobile,
|
||||
pwd,
|
||||
is_enable,
|
||||
is_non_expire,
|
||||
is_non_lock,
|
||||
wechat_id,
|
||||
qq_id,
|
||||
weibo_id,
|
||||
nickname,
|
||||
head,
|
||||
email,
|
||||
sex,
|
||||
roles,
|
||||
pay_pwd,
|
||||
reg_type,
|
||||
source_head)
|
||||
VALUES ('119d8c62b8b04154a073b3f6c3d9e14f',
|
||||
'18202837563',
|
||||
'e10adc3949ba59abbe56e057f20f883e',
|
||||
't',
|
||||
't',
|
||||
't',
|
||||
'ogIXq0HrDq3kS6MAHqMI1RUqBrGw',
|
||||
NULL,
|
||||
NULL,
|
||||
'18202837563',
|
||||
'head/6dc93e2e0809426ca8a9e6ede30b0b50.JPEG',
|
||||
NULL,
|
||||
'男',
|
||||
'[ROLE_CONSUMER, ROLE_LAYER]',
|
||||
NULL,
|
||||
'S',
|
||||
'https://wx.qlogo.cn/mmopen/vi_32/MftzC3yHluDbjZ9c3sYEibUuXNkC3pha8E6pibZO3Wh0Zop0bqHLcltjmrENc4R8Xm6oJECQGibAxZot1v9PR1hsw/132');
|
|
@ -0,0 +1,42 @@
|
|||
CREATE TABLE consumer (
|
||||
consumer_id varchar(32) NOT NULL DEFAULT NULL::character varying,
|
||||
mobile varchar(50) NOT NULL DEFAULT NULL::character varying,
|
||||
pwd varchar(32) DEFAULT NULL::character varying,
|
||||
is_enable bool DEFAULT true,
|
||||
is_non_expire bool DEFAULT true,
|
||||
is_non_lock bool DEFAULT true,
|
||||
wechat_id varchar(50) DEFAULT NULL::character varying,
|
||||
qq_id varchar(50) DEFAULT NULL::character varying,
|
||||
weibo_id varchar(50) DEFAULT NULL::character varying,
|
||||
nickname varchar(50) DEFAULT NULL::character varying,
|
||||
head varchar(255) DEFAULT NULL::character varying,
|
||||
email varchar(32) DEFAULT NULL::character varying,
|
||||
sex varchar(1) DEFAULT NULL::character varying,
|
||||
roles varchar(255),
|
||||
pay_pwd varchar(32) DEFAULT NULL::character varying,
|
||||
reg_type varchar(10) ,
|
||||
source_head varchar(255)
|
||||
)
|
||||
;
|
||||
COMMENT ON COLUMN consumer.consumer_id IS '用户id';
|
||||
COMMENT ON COLUMN consumer.mobile IS '手机号';
|
||||
COMMENT ON COLUMN consumer.pwd IS '密码:md5';
|
||||
COMMENT ON COLUMN consumer.is_enable IS '账号是否启用';
|
||||
COMMENT ON COLUMN consumer.is_non_expire IS '账号是否没有过期';
|
||||
COMMENT ON COLUMN consumer.is_non_lock IS '账号是否没有被锁定';
|
||||
COMMENT ON COLUMN consumer.wechat_id IS '微信id';
|
||||
COMMENT ON COLUMN consumer.qq_id IS 'qq id';
|
||||
COMMENT ON COLUMN consumer.weibo_id IS '微博id';
|
||||
COMMENT ON COLUMN consumer.nickname IS '昵称';
|
||||
COMMENT ON COLUMN consumer.head IS '用户头像';
|
||||
COMMENT ON COLUMN consumer.email IS '用户邮箱';
|
||||
COMMENT ON COLUMN consumer.sex IS '用户姓别 ''男'',‘女’';
|
||||
COMMENT ON COLUMN consumer.roles IS '角色、权限';
|
||||
COMMENT ON COLUMN consumer.pay_pwd IS '支付密码';
|
||||
COMMENT ON COLUMN consumer.reg_type IS '注册方式';
|
||||
COMMENT ON COLUMN consumer.source_head IS '第三方源头像路径';
|
||||
|
||||
-- ----------------------------
|
||||
-- Primary Key structure for table consumer
|
||||
-- ----------------------------
|
||||
ALTER TABLE consumer ADD CONSTRAINT consumer_pkey PRIMARY KEY (consumer_id);
|
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.yexuejc.springboot.base.mapper.ConsumerMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.yexuejc.springboot.base.security.domain.Consumer">
|
||||
<id column="consumer_id" property="consumerId"/>
|
||||
<result column="mobile" property="mobile"/>
|
||||
<result column="pwd" property="pwd"/>
|
||||
<result column="is_enable" property="enable"/>
|
||||
<result column="is_non_expire" property="nonExpire"/>
|
||||
<result column="is_non_lock" property="nonLock"/>
|
||||
<result column="wechat_id" property="wechatId"/>
|
||||
<result column="qq_id" property="qqId"/>
|
||||
<result column="weibo_id" property="weiboId"/>
|
||||
<result column="nickname" property="nickname"/>
|
||||
<result column="head" property="head"/>
|
||||
<result column="email" property="email"/>
|
||||
<result column="sex" property="sex"/>
|
||||
<result column="roles" property="roles"
|
||||
typeHandler="com.yexuejc.springboot.base.mapper.handler.JsonTypeHandler"/>
|
||||
<result column="pay_pwd" property="payPwd"/>
|
||||
<result column="reg_type" property="regType"/>
|
||||
<result column="source_head" property="sourceHead"/>
|
||||
</resultMap>
|
||||
|
||||
<insert id="insert">
|
||||
INSERT INTO consumer (consumer_id,
|
||||
mobile,
|
||||
pwd,
|
||||
is_enable,
|
||||
is_non_expire,
|
||||
is_non_lock,
|
||||
wechat_id,
|
||||
qq_id,
|
||||
weibo_id,
|
||||
nickname,
|
||||
head,
|
||||
email,
|
||||
sex,
|
||||
roles,
|
||||
pay_pwd,
|
||||
reg_type,
|
||||
source_head)
|
||||
VALUES (#{consumerId},
|
||||
#{mobile},
|
||||
#{pwd},
|
||||
#{enable},
|
||||
#{nonExpire},
|
||||
#{nonLock},
|
||||
#{wechatId},
|
||||
#{qqId},
|
||||
#{weiboId},
|
||||
#{nickname},
|
||||
#{head},
|
||||
#{email},
|
||||
#{sex},
|
||||
#{roles,typeHandler=com.yexuejc.springboot.base.mapper.handler.JsonTypeHandler},
|
||||
#{payPwd},
|
||||
#{regType},
|
||||
#{sourceHead});
|
||||
</insert>
|
||||
|
||||
<select id="selectOne" resultMap="BaseResultMap">
|
||||
select * from consumer
|
||||
<where>${ew.sqlSegment}</where>
|
||||
</select>
|
||||
|
||||
<update id="updateRoles">
|
||||
update consumer
|
||||
<set>
|
||||
<if test='et.roles!=null'>
|
||||
roles = #{et.roles,typeHandler=com.yexuejc.springboot.base.mapper.handler.JsonTypeHandler},
|
||||
</if>
|
||||
mdfy_time=now(),
|
||||
mdfy_by=#{et.mdfyBy}
|
||||
</set>
|
||||
where
|
||||
consumer_id=#{et.consumerId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue