diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/autoconfigure/OssFacade.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/autoconfigure/OssFacade.java
index df15828..a17ef69 100644
--- a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/autoconfigure/OssFacade.java
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/autoconfigure/OssFacade.java
@@ -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);
}
}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/BizConsts.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/BizConsts.java
index 1797355..dd401d7 100644
--- a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/BizConsts.java
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/BizConsts.java
@@ -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";
}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/DictRegTypeConsts.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/DictRegTypeConsts.java
new file mode 100644
index 0000000..b96de86
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/DictRegTypeConsts.java
@@ -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() {
+ }
+
+
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/LogTypeConsts.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/LogTypeConsts.java
new file mode 100644
index 0000000..1a8cfea
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/constant/LogTypeConsts.java
@@ -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";
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/exception/ThirdPartyAuthorizationException.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/exception/ThirdPartyAuthorizationException.java
new file mode 100644
index 0000000..6cf3f17
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/exception/ThirdPartyAuthorizationException.java
@@ -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;
+ }
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerAuthenticationProcessingFilter.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerAuthenticationProcessingFilter.java
new file mode 100644
index 0000000..55367f1
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerAuthenticationProcessingFilter.java
@@ -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.
+ *
+ * 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 AuthenticationDao
will need to
+ * generate the expected password in a corresponding manner.
+ *
+ *
+ * @param request so that request attributes can be retrieved
+ * @return the password that will be presented in the Authentication
+ * request token to the AuthenticationManager
+ */
+ 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 Authentication
+ * request token to the AuthenticationManager
+ */
+ 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
+ * unsuccessfulAuthentication() method will be called as if handling a failed
+ * authentication.
+ *
+ * Defaults to true 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;
+ }
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerAuthenticationProvider.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerAuthenticationProvider.java
new file mode 100644
index 0000000..09494a1
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerAuthenticationProvider.java
@@ -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;
+
+/**
+ *
主要做验证用户使用
+ *
+ * 密码登录:校验账号密码
+ * 短信登录:校验短信验证码
+ * 第三方登录:校验openid=>用户中心(数据库)不存在,进行新增数据操作
+ *
+ *
+ * @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 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 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 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;
+ }
+}
\ No newline at end of file
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerSecurityContextRepository.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerSecurityContextRepository.java
new file mode 100644
index 0000000..43d8782
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerSecurityContextRepository.java
@@ -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 上下文配置
+ *
+ * 校验token=>返回token携带用户(登录用户)信息
+ *
+ *
+ * @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 redisTemplate0;
+
+ public ConsumerSecurityContextRepository(RedisTemplate 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.
+ * Title: saveContext
+ * Description:Stores the security context on completion of a request.
+ *
+ * @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;
+ }
+
+ /**
+ *
+ * 过滤登录认证
+ * 默认:header=>Authorization:token
+ * token 为 jwt串
+ * 此处使用登录返回的token解析到具体的登录用户,返回登录用户信息;如果没有,者无权限请求该接口
+ *
+ *
+ * @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 entry = redisTemplate0.opsForHash()
+ .entries(BizConsts.CONSUMER_LOGIN_REDIS + "." + consumerToken.getUsername());
+ if (entry == null) {
+ return null;
+ }
+ if (!token.equals(entry.get(TOKEN))) {
+ return null;
+ }
+ // 处理用户权限
+ List authorities = new ArrayList<>();
+ for (String role : (List) 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;
+ }
+
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerToken.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerToken.java
new file mode 100644
index 0000000..0f43a18
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerToken.java
@@ -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
+ * 用于存放授权后,需要提取的用户信息
+ *
+ * @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;
+ }
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerUser.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerUser.java
new file mode 100644
index 0000000..bdd16b0
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/ConsumerUser.java
@@ -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;
+ }
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/LoginToken.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/LoginToken.java
new file mode 100644
index 0000000..4747153
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/LoginToken.java
@@ -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;
+ }
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/SecurityConfig.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/SecurityConfig.java
new file mode 100644
index 0000000..4b2c4d8
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/SecurityConfig.java
@@ -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 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;
+ }
+
+ /**
+ *
+ * 处理登录
+ * 成功: filter.setAuthenticationSuccessHandler()
+ * 失败: filter.setAuthenticationFailureHandler()
+ *
+ *
+ * @param filter
+ */
+ protected abstract void loginHodler(ConsumerAuthenticationProcessingFilter filter);
+
+
+ @Bean
+ public LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint() {
+ LoginUrlAuthenticationEntryPoint loginUrlAuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint
+ ("/login");
+ return loginUrlAuthenticationEntryPoint;
+ }
+
+ /**
+ * 解决跨域问题
+ *
+ * http.csrf().disable()
+ * .cors()
+ * .and()
+ *
+ * 参考 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();
+ }
+
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/UserDetailsManager.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/UserDetailsManager.java
new file mode 100644
index 0000000..5fb04aa
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/UserDetailsManager.java
@@ -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 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;
+ }
+
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/inte/User.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/inte/User.java
new file mode 100644
index 0000000..c0aba9a
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/inte/User.java
@@ -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 getRoles();
+
+
+ String getMobile();
+
+ String getPwd();
+
+ boolean getEnable();
+
+ boolean getNonExpire();
+
+ boolean getNonLock();
+
+ String getConsumerId();
+}
diff --git a/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/inte/UserService.java b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/inte/UserService.java
new file mode 100644
index 0000000..599f78f
--- /dev/null
+++ b/yexuejc-springboot-base/src/main/java/com/yexuejc/springboot/base/security/inte/UserService.java
@@ -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);
+}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/ApplicationRun.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/ApplicationRun.java
index e46a16b..13c04ba 100644
--- a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/ApplicationRun.java
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/ApplicationRun.java
@@ -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);
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/mapper/ConsumerMapper.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/mapper/ConsumerMapper.java
new file mode 100644
index 0000000..2a0e76a
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/mapper/ConsumerMapper.java
@@ -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;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author yexuejc
+ * @since 2018-05-27
+ */
+@Mapper
+public interface ConsumerMapper extends BaseMapper {
+
+ /**
+ *
+ * 插入一条记录
+ *
+ *
+ * @param entity 实体对象
+ * @return int
+ */
+ @Override
+ int insert(Consumer entity);
+
+
+ /**
+ * 修改权限
+ *
+ * @param entity
+ * @return
+ */
+ Integer updateRoles(@Param(Constants.ENTITY) Consumer entity);
+}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/mapper/handler/JsonTypeHandler.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/mapper/handler/JsonTypeHandler.java
new file mode 100644
index 0000000..50a256a
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/mapper/handler/JsonTypeHandler.java
@@ -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 {
+
+ @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);
+ }
+
+}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/MySecurityConfig.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/MySecurityConfig.java
new file mode 100644
index 0000000..59ac01d
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/MySecurityConfig.java
@@ -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 redisTemplate0;
+
+ /**
+ * 保存登录信息至redis
+ *
+ * @param redisTemplate0 redis 链接
+ * @param user 登录用户
+ * @param roles 角色信息
+ * @param token 登录token
+ * @param isPast 是否设置过期
+ */
+ private void saveLoginUser(RedisTemplate redisTemplate0, ConsumerUser user, List roles, String token,
+ boolean isPast) {
+ Map 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);
+ }
+ }
+
+ /**
+ *
+ * 处理登录
+ * 成功: filter.setAuthenticationSuccessHandler()
+ * 失败: filter.setAuthenticationFailureHandler()
+ *
+ *
+ * @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 roles = new ArrayList<>();
+ if (authorities != null && authorities.size() > 0) {
+ for (GrantedAuthority g : authorities) {
+ roles.add(g.getAuthority());
+ }
+ }
+ Map 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();
+ });
+ }
+}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/UserServiceImpl.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/UserServiceImpl.java
new file mode 100644
index 0000000..06f0c0c
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/UserServiceImpl.java
@@ -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 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 根据自己业务做判断
+ *
+ * 返回:封装登录用户信息到 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 第三方授权异常}
+ *
+ * 返回:封装登录用户信息到 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 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 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;
+ }
+
+}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/domain/Consumer.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/domain/Consumer.java
new file mode 100644
index 0000000..5ab3da0
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/domain/Consumer.java
@@ -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;
+
+/**
+ *
+ *
+ *
+ *
+ * @author yexuejc
+ * @since 2018-05-27
+ */
+@TableName(resultMap = "BaseResultMap")
+public class Consumer extends Model 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 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 getRoles() {
+ return roles;
+ }
+
+ public Consumer setRoles(List 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;
+ }
+}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/IndexCtrl.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/IndexCtrl.java
index 451bd18..943b957 100644
--- a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/IndexCtrl.java
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/IndexCtrl.java
@@ -94,4 +94,5 @@ public class IndexCtrl {
return Resps.success().setSucc(map);
}
+
}
diff --git a/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/SecurityCtrl.java b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/SecurityCtrl.java
new file mode 100644
index 0000000..19a486c
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/web/SecurityCtrl.java
@@ -0,0 +1,22 @@
+package com.yexuejc.springboot.base.web;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * Security 登录注册相关controller
+ * 主要实现
+ * 1.短信登录发送验证码
+ * 2.第三方登录绑定手机号(以及绑定手机号发送验证码)
+ *
+ *
+ * @author maxf
+ * @version 1.0
+ * @ClassName SecurityCtrl
+ * @Description
+ * @date 2018/11/9 10:52
+ */
+@RestController
+public class SecurityCtrl {
+
+}
diff --git a/yexuejc-springboot-base/src/test/resources/application.properties b/yexuejc-springboot-base/src/test/resources/application.properties
index 74813f1..b7da733 100644
--- a/yexuejc-springboot-base/src/test/resources/application.properties
+++ b/yexuejc-springboot-base/src/test/resources/application.properties
@@ -1,38 +1,36 @@
server.port=8888
-
spring.application.name=@pom.artifactId@
+#log
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
+#ǷHTTPSSSL֤֤ԣĬfalse
yexuejc.enable.ssl-ignore=true
-#reids
-spring.redis.host=121.42.165.89
-spring.redis.password=
-spring.redis.port=16379
+
+
#mns
@@ -46,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
+#ָredisdb0ĬϿ
+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
+#========================================================================================================================
diff --git a/yexuejc-springboot-base/src/test/resources/db/data.sql b/yexuejc-springboot-base/src/test/resources/db/data.sql
new file mode 100644
index 0000000..bae2350
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/resources/db/data.sql
@@ -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');
diff --git a/yexuejc-springboot-base/src/test/resources/db/schema.sql b/yexuejc-springboot-base/src/test/resources/db/schema.sql
new file mode 100644
index 0000000..dc9f36d
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/resources/db/schema.sql
@@ -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);
diff --git a/yexuejc-springboot-base/src/test/resources/mapper/ConsumerMapper.xml b/yexuejc-springboot-base/src/test/resources/mapper/ConsumerMapper.xml
new file mode 100644
index 0000000..a4c4eab
--- /dev/null
+++ b/yexuejc-springboot-base/src/test/resources/mapper/ConsumerMapper.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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});
+
+
+
+ select * from consumer
+ ${ew.sqlSegment}
+
+
+
+ update consumer
+
+
+ roles = #{et.roles,typeHandler=com.yexuejc.springboot.base.mapper.handler.JsonTypeHandler},
+
+ mdfy_time=now(),
+ mdfy_by=#{et.mdfyBy}
+
+ where
+ consumer_id=#{et.consumerId}
+
+
+