springboot security 登录(含账号、短信登录、第三方登录)模块

This commit is contained in:
maxf 2018-11-09 15:03:49 +08:00
parent 7fb712c596
commit 35d4a6be84
28 changed files with 2534 additions and 14 deletions

View File

@ -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>

View File

@ -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);
}
}

View File

@ -63,4 +63,5 @@ public class OssProperties {
this.bucket = bucket;
}
}

View File

@ -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";
}

View File

@ -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() {
}
}

View File

@ -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";
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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) {
//对于没有绑定手机号的token10分钟后过期
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();
});
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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
#是否开启HTTPSSSL请求证书验证忽略默认false
#是否开启HTTPSSSL请求证书验证忽略默认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
#========================================================================================================================

View File

@ -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');

View File

@ -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);

View File

@ -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>