手机端API认证
diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties
index 5ea16f5..61b2df9 100644
--- a/config/application-devel-pg.properties
+++ b/config/application-devel-pg.properties
@@ -24,4 +24,8 @@
 security.request.sign=false
 ##################################################
 ## quartz task scheduler
-shopbalance.updater.cron=-
\ No newline at end of file
+<<<<<<< HEAD
+shopbalance.updater.cron=-
+=======
+shopbalance.updater.cron = -
+>>>>>>> 手机端API认证
diff --git a/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java b/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
index 16e429a..7255657 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
@@ -1,16 +1,12 @@
 package com.supwisdom.dlpay.framework.filter;

 

 

-import com.fasterxml.jackson.databind.ObjectMapper;

-import com.supwisdom.dlpay.api.bean.JsonResult;

 import com.supwisdom.dlpay.exception.ValidateCodeException;

+import com.supwisdom.dlpay.framework.security.MyAuthenticationFailureHandler;

 import com.supwisdom.dlpay.framework.security.validate.ImageCodeUtil;

 import com.supwisdom.dlpay.framework.security.validate.VerifyCode;

 import com.supwisdom.dlpay.framework.util.StringUtil;

 import org.springframework.beans.factory.annotation.Autowired;

-import org.springframework.http.HttpStatus;

-import org.springframework.security.web.authentication.AuthenticationFailureHandler;

-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

 import org.springframework.stereotype.Component;

 import org.springframework.web.filter.OncePerRequestFilter;

 

@@ -22,65 +18,69 @@
 

 

 @Component("validateCodeFilter")

-public class ValidateCodeFilter extends OncePerRequestFilter{

+public class ValidateCodeFilter extends OncePerRequestFilter {

 

-	/**

-	 * 校验失败处理器

-	 */

-	@Autowired

-	private AuthenticationFailureHandler myAuthenticationFailureHandler;

-

-	/**

-	 * 校验成功处理器

-	 */

-	@Autowired

-	private AuthenticationSuccessHandler myAuthenticationSuccessHandler;

-	@Autowired

-	private ObjectMapper objectMapper;

+    /**

+     * 校验失败处理器

+     */

+    @Autowired

+    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

 

 

-	@Override

-	protected void doFilterInternal(HttpServletRequest request,

-																	HttpServletResponse response, FilterChain filterChain)

-			throws ServletException, IOException {

-		if (StringUtil.equals("/login/form", request.getRequestURI())

-				&& StringUtil.equalsIgnoreCase(request.getMethod(), "post")) {

-			try {

-				validate(request);

-			} catch (ValidateCodeException e) {

-				//response.setTransStatus(HttpStatus.OK.value());

-				//response.setContentType("application/json;charset=UTF-8");

-				//response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, e.getMessage())));

-				//response.sendError(HttpStatus.UNAUTHORIZED.value(),e.getMessage());

-				myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);

-				return;

-			}

-		}

-		filterChain.doFilter(request, response);

-	}

+    @Override

+    protected void doFilterInternal(HttpServletRequest request,

+                                    HttpServletResponse response, FilterChain filterChain)

+            throws ServletException, IOException {

+        String context = request.getContextPath();

+        if (context == null || "" == context.trim()) {

+            context = "/";

+        }

+        if (request.getRequestURI().isEmpty()) {

+            filterChain.doFilter(request, response);

+            return;

+        }

+        String url = request.getRequestURI();

+        if (!"/".equals(context)) {

+            url = url.replace(context, "");

+        }

+        if (StringUtil.equals("/login/form", url)

+                && StringUtil.equalsIgnoreCase(request.getMethod(), "post")) {

+            try {

+                validate(request);

+            } catch (ValidateCodeException e) {

+                //response.setStatus(HttpStatus.OK.value());

+                //response.setContentType("application/json;charset=UTF-8");

+                //response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, e.getMessage())));

+                //response.sendError(HttpStatus.UNAUTHORIZED.value(),e.getMessage());

+                myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);

+                return;

+            }

+        }

+        filterChain.doFilter(request, response);

+    }

 

-	private void validate(HttpServletRequest request) throws ValidateCodeException {

-		VerifyCode imageCode = (VerifyCode) request.getSession().getAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);

-		String inputCode;

-		try {

-			inputCode = request.getParameter("imageCode");

-		} catch (Exception e) {

-			throw new ValidateCodeException("获取验证码的值失败");

-		}

-		if (StringUtil.isEmpty(inputCode)) {

-			throw new ValidateCodeException("验证码不能为空");

-		}

-		if (null == imageCode) {

-			throw new ValidateCodeException("验证码不存在");

-		}

-		if (imageCode.isExpired()) {

-			request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);

-			throw new ValidateCodeException("验证码已过期");

-		}

-		if (!StringUtil.equalsIgnoreCase(imageCode.getText(), inputCode)) {

-			throw new ValidateCodeException("验证码不匹配");

-		}

-		request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);

-	}

+    private void validate(HttpServletRequest request) throws ValidateCodeException {

+        VerifyCode imageCode = (VerifyCode) request.getSession().getAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);

+        String inputCode;

+        try {

+            inputCode = request.getParameter("imageCode");

+        } catch (Exception e) {

+            throw new ValidateCodeException("获取验证码的值失败");

+        }

+        if (StringUtil.isEmpty(inputCode)) {

+            throw new ValidateCodeException("验证码不能为空");

+        }

+        if (null == imageCode) {

+            throw new ValidateCodeException("验证码不存在");

+        }

+        if (imageCode.isExpired()) {

+            request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);

+            throw new ValidateCodeException("验证码已过期");

+        }

+        if (!StringUtil.equalsIgnoreCase(imageCode.getText(), inputCode)) {

+            throw new ValidateCodeException("验证码不匹配");

+        }

+        request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);

+    }

 

 }

diff --git a/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java
index 38a3fbe..3371b31 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java
@@ -2,6 +2,7 @@
 
 import com.supwisdom.dlpay.framework.domain.JwtRedis;
 import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
 
 public interface ApiJwtRepository extends CrudRepository<JwtRedis, String> {
 }
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
new file mode 100644
index 0000000..e1b5869
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt
@@ -0,0 +1,113 @@
+package com.supwisdom.dlpay.mobile
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.supwisdom.dlpay.api.bean.JsonResult
+import com.supwisdom.dlpay.exception.ValidateCodeException
+import com.supwisdom.dlpay.framework.core.JwtConfig
+import com.supwisdom.dlpay.framework.core.JwtTokenUtil
+import com.supwisdom.dlpay.framework.domain.JwtRedis
+import com.supwisdom.dlpay.framework.redisrepo.ApiClientRepository
+import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
+import com.supwisdom.dlpay.framework.util.DateUtil
+import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.mobile.dao.MobileUserDao
+import com.supwisdom.dlpay.mobile.domain.TBMobileUser
+import com.supwisdom.dlpay.mobile.exception.UserLoginFailException
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.http.HttpStatus
+import org.springframework.security.authentication.BadCredentialsException
+import org.springframework.security.authentication.LockedException
+import org.springframework.security.core.Authentication
+import org.springframework.security.core.AuthenticationException
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
+import org.springframework.stereotype.Component
+import java.io.IOException
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+@Component("authLoginSuccessHandler")
+class AuthLoginSuccessHandler : SimpleUrlAuthenticationSuccessHandler() {
+    @Autowired
+    lateinit var mobileUserDao: MobileUserDao
+    @Autowired
+    lateinit var objectMapper: ObjectMapper
+    @Autowired
+    lateinit var jwtConfig: JwtConfig
+    @Autowired
+    lateinit var apiJwtRepository: ApiJwtRepository
+
+    override fun onAuthenticationSuccess(request: HttpServletRequest, response: HttpServletResponse, authentication: Authentication) {
+        logger.error(request?.getParameter("platform"))
+        var temp = authentication!!.principal as TBMobileUser
+        var user = mobileUserDao.findByPhone(temp.phone)
+        if(user!=null) {
+            //TODO 从数据取jwtConfig.expiration
+            val token = JwtTokenUtil(jwtConfig).generateToken(
+                    mapOf("uid" to user.uid, "issuer" to "payapi",
+                            "audience" to temp.phone,
+                            "authorities" to temp.authorities))
+            var jwt = JwtRedis().apply {
+                jti = token.jti
+                uid = temp.phone
+                status = TradeDict.JWT_STATUS_NORMAL
+                expiration = token.expiration.valueInMillis
+            }.apply {
+                //删除之前的token
+                if(!user.jti.isNullOrEmpty()){
+                    apiJwtRepository.deleteById(user.jti!!)
+                }
+                apiJwtRepository.save(this)
+            }
+            if (user.loginpwderror != null && user.loginpwderror!! > 0) {
+                user.loginpwderror = 0
+                user.loginpwderrortime = null
+            }
+            user.lastlogin = DateUtil.getNow()
+            user.jti = jwt.jti
+            mobileUserDao.save(user)
+            response.status = HttpStatus.OK.value()
+            response.contentType = "application/json;charset=UTF-8"
+            response.writer.write(objectMapper.writeValueAsString(JsonResult.ok().put("token", token.jwtToken)))
+        }else{
+            throw UserLoginFailException("登录错误")
+        }
+    }
+}
+
+
+@Component("authLoginFailHandler")
+class AuthLoginFailHandler : SimpleUrlAuthenticationFailureHandler() {
+    @Autowired
+    lateinit var mobileUserDao: MobileUserDao
+
+    @Autowired
+    lateinit var objectMapper: ObjectMapper
+
+    @Throws(IOException::class, ServletException::class)
+    override fun onAuthenticationFailure(request: HttpServletRequest,
+                                         response: HttpServletResponse, exception: AuthenticationException) {
+        logger.error("登录失败:" + exception.message + "|" + exception.javaClass)
+        var errmsg = ""
+        if (exception is BadCredentialsException) {
+            errmsg = "手机号或密码错误"
+        } else if (exception is LockedException) {
+            errmsg = "账户被锁定"
+        } else  {
+            errmsg = exception.message!!
+        }
+        var temp = request.getParameter("username")
+        mobileUserDao.findByPhone(temp)?.let {
+            if (it.loginpwderror == null || it.loginpwderror == 0) {
+                it.loginpwderror = 0
+                it.loginpwderrortime = System.currentTimeMillis()
+            }
+            it.loginpwderror += 1
+            mobileUserDao.save(it)
+        }
+        response.status = HttpStatus.OK.value()
+        response.contentType = "application/json;charset=UTF-8"
+        response.writer.write(objectMapper.writeValueAsString(JsonResult.error(errmsg)))
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
new file mode 100644
index 0000000..a39db67
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt
@@ -0,0 +1,38 @@
+package com.supwisdom.dlpay.mobile
+
+import com.supwisdom.dlpay.api.bean.JsonResult
+import com.supwisdom.dlpay.mobile.service.MobileUserService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import org.springframework.web.bind.annotation.RequestMethod
+import java.security.Principal
+
+
+@RestController
+@RequestMapping("/mobileapi")
+class MobileApiController {
+    @Autowired
+    lateinit var userService: MobileUserService
+    @RequestMapping("/logout")
+    fun logout(request: HttpServletRequest, response: HttpServletResponse): JsonResult {
+        SecurityContextHolder.getContext().authentication?.also {
+            SecurityContextLogoutHandler().logout(request, response, it)
+        }
+        return JsonResult.ok("退出成功")
+    }
+}
+
+@RestController
+@RequestMapping("/mobileapi/v1")
+class ApiV1 {
+    @RequestMapping("/infor")
+    fun getUserInfor(): JsonResult {
+        val p =  SecurityContextHolder.getContext().authentication
+        return JsonResult.ok("OK")
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/dao/MobileUserDao.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/dao/MobileUserDao.kt
new file mode 100644
index 0000000..5261586
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/dao/MobileUserDao.kt
@@ -0,0 +1,10 @@
+package com.supwisdom.dlpay.mobile.dao
+
+import com.supwisdom.dlpay.mobile.domain.TBMobileUser
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface MobileUserDao : JpaRepository<TBMobileUser, String> {
+    fun findByPhone(phone: String): TBMobileUser?
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
new file mode 100644
index 0000000..da95e4f
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt
@@ -0,0 +1,136 @@
+package com.supwisdom.dlpay.mobile.domain
+
+import com.supwisdom.dlpay.framework.util.TradeDict
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.userdetails.UserDetails
+import javax.persistence.*
+
+@Entity
+@Table(name = "TB_MOBILE_USER")
+class TBMobileUser : UserDetails {
+    override fun getAuthorities(): Collection<GrantedAuthority>? {
+        return this.auths
+    }
+
+    override fun isEnabled(): Boolean {
+        return TradeDict.STATUS_CLOSED != this.status
+    }
+
+    override fun getUsername(): String {
+        return this.phone
+    }
+
+    override fun isCredentialsNonExpired(): Boolean {
+        return true
+    }
+
+    override fun getPassword(): String {
+        return this.loginpwd
+    }
+
+    override fun isAccountNonExpired(): Boolean {
+        return true
+    }
+
+    override fun isAccountNonLocked(): Boolean {
+        return TradeDict.STATUS_LOCKED != this.status
+    }
+    @Transient
+    var auths: Collection<GrantedAuthority>? = null
+
+
+    @Id
+    @Column(name = "uid", nullable = false, length = 32)
+    var uid: String = ""
+    /**
+     * 手机号
+     * */
+    @Column(name = "phone", length = 15)
+    var phone: String = ""
+
+    /**
+     * 设备uuid
+     * */
+    @Column(name = "devuid", length = 64)
+    var devuid: String? = null
+
+    /**
+     * 注册时间
+     * */
+    @Column(name = "registerdate", length = 16)
+    var registerdate: String? = null
+
+    /**
+     * 关联tb_person
+     * */
+    @Column(name = "userid", length = 32)
+    var userid: String? = null
+
+    /**
+     * 注册手机类型
+     * */
+    @Column(name = "registerplatform", length = 20)
+    var registerplatform: String? = null
+
+    /**
+     * 最后登录时间
+     * */
+    @Column(name = "lastlogin", length = 16)
+    var lastlogin: String? = null
+
+    /**
+     * 最后登录手机类型
+     * */
+    @Column(name = "lastloginplatform", length = 20)
+    var lastloginplatform: String? = null
+
+    /**
+     * 状态
+     * */
+    @Column(name = "status", length = 16)
+    var status: String? = null
+
+    /**
+     * 登录密码
+     * */
+    @Column(name = "loginpwd", length = 64)
+    var loginpwd: String = ""
+
+    /**
+     * 支付密码
+     * */
+    @Column(name = "paypwd", length = 64)
+    var paypwd: String? = null
+
+    /**
+     * 登录密码错误次数
+     * */
+    @Column(name = "loginpwderror", length = 4)
+    var loginpwderror: Int = 0
+
+    /**
+     * 登录密码错误时间
+     * */
+    @Column(name = "loginpwderrortime", length = 16)
+    var loginpwderrortime: Long? = 0
+
+    /**
+     * 支付密码错误次数
+     * */
+    @Column(name = "paypwderror", length = 4)
+    var paypwderror: Int = 0
+
+    /**
+     * 支付密码错误时间
+     * */
+    @Column(name = "paypwderrortime", length = 16)
+    var paypwderrortime: Long? = 0
+
+
+    /**
+     * jti
+     * */
+    @Column(name = "jti", length = 64)
+    var jti: String? = null
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/exception/UserLoginFailException.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/exception/UserLoginFailException.kt
new file mode 100644
index 0000000..67d6243
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/exception/UserLoginFailException.kt
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.mobile.exception
+
+import org.springframework.security.core.AuthenticationException
+
+class UserLoginFailException(msg: String) : AuthenticationException(msg) {
+    private val serialVersionUID = 1170189980006964105L
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
new file mode 100644
index 0000000..c01e430
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.mobile.service
+
+import com.supwisdom.dlpay.mobile.domain.TBMobileUser
+
+interface MobileApiService {
+    fun saveUser(user: TBMobileUser): TBMobileUser
+
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileUserService.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileUserService.kt
new file mode 100644
index 0000000..6d10c46
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileUserService.kt
@@ -0,0 +1,5 @@
+package com.supwisdom.dlpay.mobile.service
+
+import org.springframework.security.core.userdetails.UserDetailsService
+
+interface MobileUserService : UserDetailsService
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
new file mode 100644
index 0000000..fec5155
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt
@@ -0,0 +1,19 @@
+package com.supwisdom.dlpay.mobile.service.impl
+
+import com.supwisdom.dlpay.framework.util.DateUtil
+import com.supwisdom.dlpay.mobile.dao.MobileUserDao
+import com.supwisdom.dlpay.mobile.domain.TBMobileUser
+import com.supwisdom.dlpay.mobile.service.MobileApiService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+@Service
+class MobileApiServiceImpl : MobileApiService {
+    @Autowired
+    lateinit var mobileUserDao: MobileUserDao
+
+    override fun saveUser(user: TBMobileUser): TBMobileUser {
+        user.lastlogin = DateUtil.getNow()
+        return mobileUserDao.save(user)
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt
new file mode 100644
index 0000000..9f16334
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt
@@ -0,0 +1,37 @@
+package com.supwisdom.dlpay.mobile.service.impl
+
+import com.supwisdom.dlpay.mobile.dao.MobileUserDao
+import com.supwisdom.dlpay.mobile.domain.TBMobileUser
+import com.supwisdom.dlpay.mobile.exception.UserLoginFailException
+import com.supwisdom.dlpay.mobile.service.MobileUserService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.authority.AuthorityUtils
+import org.springframework.security.core.userdetails.UserDetails
+import org.springframework.security.core.userdetails.UsernameNotFoundException
+import org.springframework.stereotype.Service
+
+@Service
+class MobileUserServiceImpl : MobileUserService {
+    @Autowired
+    lateinit var mobileUserDao: MobileUserDao
+
+    override fun loadUserByUsername(username: String?): UserDetails {
+        var temp = mobileUserDao.findByPhone(username!!)
+        if(temp!=null) {
+            if (temp.loginpwderror != null && temp.loginpwderror!! >= 3 && (System.currentTimeMillis() - temp.loginpwderrortime!!) < 1000 * 60 * 30) {
+                throw UserLoginFailException("密码错误次数过多,请稍后再试")
+            } else if (temp.loginpwderror != null && temp.loginpwderror!! >= 3 && (System.currentTimeMillis() - temp.loginpwderrortime!!) > 1000 * 60 * 30) {
+                //更新时间
+                temp.loginpwderror = 0
+                temp.loginpwderrortime = null
+                mobileUserDao.save(temp)
+            }
+            var authorities: Collection<GrantedAuthority> = AuthorityUtils.createAuthorityList("ROLE_USER")
+            temp.auths = authorities
+        }else{
+            throw UsernameNotFoundException("用户不存在")
+        }
+        return temp
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/security.kt b/src/main/kotlin/com/supwisdom/dlpay/security.kt
index 514b80c..0914da6 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/security.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/security.kt
@@ -1,13 +1,18 @@
 package com.supwisdom.dlpay
 
+import com.supwisdom.dlpay.api.bean.JsonResult
 import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.core.JwtTokenUtil
 import com.supwisdom.dlpay.framework.core.PasswordBCryptConfig
 import com.supwisdom.dlpay.framework.redisrepo.ApiJwtRepository
+import com.supwisdom.dlpay.framework.security.MyAuthenticationFailureHandler
 import com.supwisdom.dlpay.framework.security.MyPermissionEvaluator
 import com.supwisdom.dlpay.framework.security.ValidateCodeSecurityConfig
 import com.supwisdom.dlpay.framework.service.OperatorDetailService
 import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.mobile.AuthLoginFailHandler
+import com.supwisdom.dlpay.mobile.AuthLoginSuccessHandler
+import com.supwisdom.dlpay.mobile.service.MobileUserService
 import com.supwisdom.dlpay.system.common.DictPool
 import org.jose4j.jwt.consumer.InvalidJwtException
 import org.jose4j.lang.JoseException
@@ -84,7 +89,7 @@
             url = url.replace(context,"")
         }
         logger.info(url)
-        if(!url.startsWith("/api/")){
+        if(!url.startsWith("/api/")&&!url.startsWith("/mobileapi/")){
             filterChain.doFilter(request, response)
             return
         }
@@ -101,6 +106,11 @@
                     return
                 }
                 val claims = getUtil().verifyToken(jwt)
+                if(url.equals("/mobileapi/logout")){
+                    SecurityContextHolder.clearContext()
+                    apiJwtRepository.deleteById(claims["jti"].toString())
+                    throw JoseException("JWT has not been register")
+                }
                 apiJwtRepository.findById(claims["jti"].toString()).let {
                     if (!it.isPresent) {
                         throw JoseException("JWT has not been register")
@@ -119,14 +129,15 @@
                 if (e.hasExpired()) {
                     // jwt 过期后返回 401
                     apiJwtRepository.deleteById(e.jwtContext.jwtClaims.jwtId)
-                    response.sendError(HttpStatus.UNAUTHORIZED.value(), e.message)
-                } else {
-                    response.sendError(HttpStatus.BAD_REQUEST.value(), e.message)
                 }
+                response.setStatus(HttpStatus.UNAUTHORIZED.value(), e.message)
+                return
             } catch (e: JoseException) {
                 SecurityContextHolder.clearContext()
                 // jwt 失效后返回 401
-                response.sendError(HttpStatus.UNAUTHORIZED.value(), e.message)
+                response.setStatus(HttpStatus.UNAUTHORIZED.value(), e.message)
+                response.contentType = "application/json;charset=UTF-8"
+                return
             }
         }
         filterChain.doFilter(request, response)
@@ -171,6 +182,60 @@
                 return super.authenticationManagerBean()
             }
         }
+        @Configuration
+        @Order(2)
+        class MobileApiSecurityConfigurationAdapter : WebSecurityConfigurerAdapter() {
+            @Autowired
+            lateinit var failureHandler: AuthLoginFailHandler
+            @Autowired
+            lateinit var successHandler: AuthLoginSuccessHandler
+            @Autowired
+            lateinit var passwordBCryptConfig: PasswordBCryptConfig
+
+            @Autowired
+            lateinit var userDetailsService: MobileUserService
+
+            @Autowired
+            lateinit var apiFilter: ApiJwtAuthenticationFilter
+
+            override fun configure(auth: AuthenticationManagerBuilder) {
+                auth.authenticationProvider(userProvider())
+            }
+            @Bean
+            fun userProvider(): DaoAuthenticationProvider {
+                return DaoAuthenticationProvider().apply {
+                    setUserDetailsService(userDetailsService)
+                    setPasswordEncoder(userPasswordEncoder())
+                }
+            }
+
+            @Bean
+            fun userPasswordEncoder(): BCryptPasswordEncoder {
+                return if (passwordBCryptConfig.seed.isBlank()) {
+                    BCryptPasswordEncoder()
+                } else {
+                    BCryptPasswordEncoder(passwordBCryptConfig.length,
+                            SecureRandom(passwordBCryptConfig.seed.toByteArray()))
+                }
+            }
+
+            override fun configure(http: HttpSecurity) {
+                http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                        .and()
+                        .antMatcher("/mobileapi/**")
+                        .addFilterAfter(apiFilter,
+                                UsernamePasswordAuthenticationFilter::class.java)
+                        .authorizeRequests().anyRequest().authenticated()
+                        .and()
+                        .formLogin()
+                        .loginProcessingUrl("/mobileapi/login")
+                        .failureHandler(failureHandler)
+                        .successHandler(successHandler)
+                        .and().csrf().disable()
+                        .sessionManagement().maximumSessions(1)
+                        .expiredUrl("/mobileapi/sessionexpired")
+            }
+        }
 
         @Configuration
         class MvcWebSecurityConfigurationAdapter : WebSecurityConfigurerAdapter() {
@@ -179,7 +244,7 @@
             @Autowired
             lateinit var validateCodeSecurityConfig: ValidateCodeSecurityConfig
             @Autowired
-            lateinit var authenticationFailureHandler: AuthenticationFailureHandler
+            lateinit var authenticationFailureHandler: MyAuthenticationFailureHandler
             @Autowired
             lateinit var passwordBCryptConfig: PasswordBCryptConfig
 
@@ -220,7 +285,7 @@
                 http.apply(validateCodeSecurityConfig)
                         .and()
                         .authorizeRequests()
-                        .antMatchers("/login", "/login/form").permitAll()
+                        .antMatchers("/login", "/login/form","/mobileapi/**").permitAll()
                         .antMatchers("/static/**").permitAll()
                         .antMatchers("/code/image").permitAll()
                         .antMatchers("/**").hasAnyRole("USER", "ADMIN")