diff --git a/README.md b/README.md
index ac5f88b..76bdef5 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,8 @@ parent:版本封装
base:功能封装
#### 最新版本
-* 1.x yexuejc.springboot.version=1.1.4
-* 2.x yexuejc.springboot.version=2.0.3
-* yexuejc.base.version=1.2.1
+* yexuejc.springboot.version=1.2.0
+* yexuejc.base.version=1.2.4
pom.xml
```
diff --git a/UPDATE.md b/UPDATE.md
index 4ecdf5a..286fe53 100644
--- a/UPDATE.md
+++ b/UPDATE.md
@@ -1,6 +1,20 @@
yexuejc-springboot 更新内容
-------------------
+#### version :1.2.0
+**time:2018-12-1 12:19:06**
+**branch:** master
+**关联工程:**
+```
+springboot-base:1.2.4
+spring-boot-starter-parent:1.5.16.RELEASE
+```
+**update:**
+1. security多方登录第一个稳定版
+支持账号登录、短信登录、第三方授权openid登录
+功能链接[security重构-多方登录](doc/SECURITY.md)
+#
+
#### version :1.1.6-1.1.9
**time:2018-11-21 15:03:01**
**branch:** master
diff --git a/doc/SECURITY.md b/doc/SECURITY.md
index 0ad013c..8eeee24 100644
--- a/doc/SECURITY.md
+++ b/doc/SECURITY.md
@@ -1,5 +1,7 @@
-Security框架封装集成登录 使用指南
+Security框架封装集成多方登录 使用指南
-------------
+#### 先上[效果图](Securtity效果图.md)
+
单独使用例子工程:[https://github.com/yexuejc/springboot-security-login-simple](https://github.com/yexuejc/springboot-security-login-simple)
* 本项目依赖不向下传递
@@ -15,7 +17,8 @@ Security框架封装集成登录 使用指南
```
> **相关文件说明** 所有核心文件都在 com.yexuejc.springboot.base.security 包下
-
+#### 现附上系统实现逻辑图
+
1.com.yexuejc.springboot.base.security.SecurityConfig
@@ -26,6 +29,38 @@ Security框架封装集成登录 使用指南
* 继承configure(HttpSecurity http) 完善更多security过滤配置
* 例子[com.yexuejc.springboot.base.security.MySecurityConfig](../yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/MySecurityConfig.java)
+#### 注: 代码中抛出的相关异常拦截在filter.setAuthenticationFailureHandler()中处理,参考[MySecurityConfig](../yexuejc-springboot-base/src/test/java/com/yexuejc/springboot/base/security/MySecurityConfig.java)
+```
+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 if (exception instanceof UserNotAuthoriayException) {
+ resps.setErr(RespsConsts.CODE_FAIL, new String[]{exception.getMessage()});
+ } else {
+ resps.setErr(RespsConsts.CODE_FAIL, new String[]{BizConsts.BASE_SYS_ERR_MSG});
+ }
+ response.getWriter().write(JsonUtil.obj2Json(resps));
+ response.getWriter().close();
+ });
+```
+
2.com.yexuejc.springboot.base.security.UserDetailsManager
**获取登录用户信息**
diff --git a/doc/Securtity效果图.md b/doc/Securtity效果图.md
new file mode 100644
index 0000000..dcdf1af
--- /dev/null
+++ b/doc/Securtity效果图.md
@@ -0,0 +1,24 @@
+Security 多方登录封装使用效果图
+---------------
+### 账号登录
+密码错误
+
+正确
+
+
+### 短信登录
+发送短信
+
+短信错误
+
+短信过期
+
+正确
+
+
+
+### 第三方登录
+第一次登录,需要绑定手机号
+
+绑定过手机号的第三方账号登录(绑定相关业务需要自己实现)
+
\ No newline at end of file
diff --git a/doc/sl/sl_01.png b/doc/sl/sl_01.png
new file mode 100644
index 0000000..4301520
Binary files /dev/null and b/doc/sl/sl_01.png differ
diff --git a/doc/sl/sl_02.png b/doc/sl/sl_02.png
new file mode 100644
index 0000000..6325819
Binary files /dev/null and b/doc/sl/sl_02.png differ
diff --git a/doc/sl/sl_10.png b/doc/sl/sl_10.png
new file mode 100644
index 0000000..f77936a
Binary files /dev/null and b/doc/sl/sl_10.png differ
diff --git a/doc/sl/sl_err.jpg b/doc/sl/sl_err.jpg
new file mode 100644
index 0000000..1910cf0
Binary files /dev/null and b/doc/sl/sl_err.jpg differ
diff --git a/doc/sl/sl_gq.png b/doc/sl/sl_gq.png
new file mode 100644
index 0000000..9079cd5
Binary files /dev/null and b/doc/sl/sl_gq.png differ
diff --git a/doc/sl/sl_ss.png b/doc/sl/sl_ss.png
new file mode 100644
index 0000000..842a277
Binary files /dev/null and b/doc/sl/sl_ss.png differ
diff --git a/doc/sl/sl_t3.png b/doc/sl/sl_t3.png
new file mode 100644
index 0000000..e9ffd05
Binary files /dev/null and b/doc/sl/sl_t3.png differ
diff --git a/doc/sl/sl_t4.png b/doc/sl/sl_t4.png
new file mode 100644
index 0000000..850d1d8
Binary files /dev/null and b/doc/sl/sl_t4.png differ
diff --git a/doc/多方登录设计.jpg b/doc/多方登录设计.jpg
new file mode 100644
index 0000000..9f116ab
Binary files /dev/null and b/doc/多方登录设计.jpg differ
diff --git a/pom.xml b/pom.xml
index 5d700b9..9ca6956 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.yexuejc.springboot
yexuejc-springboot-parent
- 1.1.9
+ 1.2.0
pom
${project.artifactId}
diff --git a/yexuejc-springboot-base/pom.xml b/yexuejc-springboot-base/pom.xml
index 74daa6c..1dc0d30 100644
--- a/yexuejc-springboot-base/pom.xml
+++ b/yexuejc-springboot-base/pom.xml
@@ -9,7 +9,7 @@
com.yexuejc.springboot
yexuejc-springboot-parent
- 1.1.9
+ 1.2.0
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
index 844aac3..d8b7623 100644
--- 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
@@ -13,11 +13,12 @@ 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.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@@ -44,28 +45,42 @@ public class UserServiceImpl implements UserService {
/**
* 根据用户名到数据库查询用户
- *
- * 账号未找到,抛出UsernameNotFoundException异常=>会走第三方登录流程
- *
*
* @param username 登录账号
* @return
*/
@Override
- public User getConsumerByUserName(String username) {
+ public Object getConsumerByUserName(String username) {
if (StrUtil.isEmpty(username)) {
- throw new UsernameNotFoundException(username);
+ throw new UsernameNotFoundException("username为空,一般是第三方登录来的,直接抛出UsernameNotFoundException就是");
}
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("mobile", username);
Consumer consumer = consumerMapper.selectOne(queryWrapper);
if (null == consumer) {
- throw new UsernameNotFoundException(username);
+ /**
+ * 1.抛出UsernameNotFoundException这个异常如果是第三方登录会走 {@link #checkOpenId(ConsumerToken)}
+ * 2.抛出其他Exception可以自己到{@link MySecurityConfig#loginHodler(ConsumerAuthenticationProcessingFilter)}
+ * 里面的filter.setAuthenticationFailureHandler()中做特殊处理
+ */
+ throw new UsernameNotFoundException("没有该账号相关信息");
}
+ //h2不支持json,人为处理角色
ArrayList roles = new ArrayList<>();
roles.add("ROLE_CONSUMER");
consumer.setRoles(roles);
- return consumer;
+ //1.consumer为User的实现类
+// return consumer;
+
+ //2. 自己创建ConsumerUser,直接返回
+ 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;
}
/**
@@ -98,15 +113,14 @@ public class UserServiceImpl implements UserService {
}
/**
+ * 第三方登录
* 校验openid 根据自己业务做判断
- *
- * 返回:封装登录用户信息到 apiVO.setObject1(User.class) 自己封装登录用户信息
*
* @param consumerToken 登录信息
* @return
*/
@Override
- public ApiVO checkOpenId(ConsumerToken consumerToken) {
+ public Object checkOpenId(ConsumerToken consumerToken) {
ApiVO apiVO = new ApiVO(ApiVO.STATUS.F, "没有找到用户信息");
switch (consumerToken.getLogtype()) {
case LogTypeConsts.QQ:
@@ -121,9 +135,89 @@ public class UserServiceImpl implements UserService {
default:
break;
}
- return apiVO;
+ if (apiVO.isFail()) {
+ /**
+ * 未查到:
+ * 1.返回null会走(数据库没有这个openid[第三方账号]信息)新增流程 {@link #addConsumer(ConsumerToken)}
+ * 2.也可以自己创建一个带有特殊标识的ConsumerUser,然后在 {@link MySecurityConfig#loginHodler(ConsumerAuthenticationProcessingFilter)}
+ * 里面的filter.setAuthenticationSuccessHandler()中做特殊处理 ps:假装登录成功 :)
+ */
+ return null;
+ }
+ //h2不支持json,人为处理角色
+ ArrayList roles = new ArrayList<>();
+ roles.add("ROLE_CONSUMER");
+ apiVO.getObject1(Consumer.class).setRoles(roles);
+ //根据openid到数据库查到consumer返回
+ return apiVO.getObject1(Consumer.class);
}
+ /**
+ * {@link #checkOpenId(ConsumerToken)} 返回null会走该方法
+ * 没有账号时处理自己的业务,此处必须返回 构造出的登录用户,否则会抛出{@link ThirdPartyAuthorizationException 第三方授权异常}
+ *
+ *
+ * @param consumerToken 登录信息
+ * @return
+ */
+ @Override
+ public Object 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()) {
+ throw new ThirdPartyAuthorizationException("短信验证码错误");
+ }
+ 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:
+ throw new ThirdPartyAuthorizationException("暂不支持该第三方授权");
+ }
+ Integer result = consumerMapper.insert(consumer);
+ if (result < 1) {
+ /**
+ * 会抛出{@link ThirdPartyAuthorizationException 第三方授权异常}
+ */
+ return null;
+ }
+ return consumer;
+ }
+
+
/**
* 第三方登录 QQ登录
*
@@ -183,69 +277,6 @@ public class UserServiceImpl implements UserService {
}
- /**
- * 没有账号时处理自己的业务,此次必须返回 构造出的登录用户,否则会抛出{@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);
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
index 8f5ea6f..549faf9 100644
--- 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
@@ -1,7 +1,23 @@
package com.yexuejc.springboot.base.web;
+import com.yexuejc.base.http.Resps;
+import com.yexuejc.base.pojo.ApiVO;
+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 org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
/**
*
* Security 登录注册相关controller
@@ -19,5 +35,49 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class SecurityCtrl {
+ @Autowired
+ @Qualifier(MutiRedisAutoConfiguration.BEAN_REDIS_TEMPLATE1)
+ RedisTemplate