手机端API认证
authorqiaowei <jov123@163.com>
Wed, 26 Jun 2019 07:11:32 +0000 (15:11 +0800)
committerqiaowei <jov123@163.com>
Wed, 26 Jun 2019 07:13:08 +0000 (15:13 +0800)
13 files changed:
config/application-devel-pg.properties
src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
src/main/java/com/supwisdom/dlpay/framework/redisrepo/ApiJwtRepository.java
src/main/kotlin/com/supwisdom/dlpay/mobile/AuthLoginHandler.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/MobileApi.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/dao/MobileUserDao.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/domain/TBMobileUser.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/exception/UserLoginFailException.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileApiService.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/service/MobileUserService.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileApiServiceImpl.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/mobile/service/impl/MobileUserServiceImpl.kt [new file with mode: 0644]
src/main/kotlin/com/supwisdom/dlpay/security.kt

index 5ea16f5..61b2df9 100644 (file)
@@ -24,4 +24,8 @@ logging.level.org.springframework.web=DEBUG
 security.request.sign=false
 ##################################################
 ## quartz task scheduler
-shopbalance.updater.cron=-
\ No newline at end of file
+<<<<<<< HEAD
+shopbalance.updater.cron=-
+=======
+shopbalance.updater.cron = -
+>>>>>>> 手机端API认证
index 16e429a..7255657 100644 (file)
@@ -1,16 +1,12 @@
 package com.supwisdom.dlpay.framework.filter;\r
 \r
 \r
-import com.fasterxml.jackson.databind.ObjectMapper;\r
-import com.supwisdom.dlpay.api.bean.JsonResult;\r
 import com.supwisdom.dlpay.exception.ValidateCodeException;\r
+import com.supwisdom.dlpay.framework.security.MyAuthenticationFailureHandler;\r
 import com.supwisdom.dlpay.framework.security.validate.ImageCodeUtil;\r
 import com.supwisdom.dlpay.framework.security.validate.VerifyCode;\r
 import com.supwisdom.dlpay.framework.util.StringUtil;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
-import org.springframework.http.HttpStatus;\r
-import org.springframework.security.web.authentication.AuthenticationFailureHandler;\r
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;\r
 import org.springframework.stereotype.Component;\r
 import org.springframework.web.filter.OncePerRequestFilter;\r
 \r
@@ -22,65 +18,69 @@ import java.io.IOException;
 \r
 \r
 @Component("validateCodeFilter")\r
-public class ValidateCodeFilter extends OncePerRequestFilter{\r
+public class ValidateCodeFilter extends OncePerRequestFilter {\r
 \r
-       /**\r
-        * 校验失败处理器\r
-        */\r
-       @Autowired\r
-       private AuthenticationFailureHandler myAuthenticationFailureHandler;\r
+    /**\r
+     * 校验失败处理器\r
+     */\r
+    @Autowired\r
+    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;\r
 \r
-       /**\r
-        * 校验成功处理器\r
-        */\r
-       @Autowired\r
-       private AuthenticationSuccessHandler myAuthenticationSuccessHandler;\r
-       @Autowired\r
-       private ObjectMapper objectMapper;\r
 \r
+    @Override\r
+    protected void doFilterInternal(HttpServletRequest request,\r
+                                    HttpServletResponse response, FilterChain filterChain)\r
+            throws ServletException, IOException {\r
+        String context = request.getContextPath();\r
+        if (context == null || "" == context.trim()) {\r
+            context = "/";\r
+        }\r
+        if (request.getRequestURI().isEmpty()) {\r
+            filterChain.doFilter(request, response);\r
+            return;\r
+        }\r
+        String url = request.getRequestURI();\r
+        if (!"/".equals(context)) {\r
+            url = url.replace(context, "");\r
+        }\r
+        if (StringUtil.equals("/login/form", url)\r
+                && StringUtil.equalsIgnoreCase(request.getMethod(), "post")) {\r
+            try {\r
+                validate(request);\r
+            } catch (ValidateCodeException e) {\r
+                //response.setStatus(HttpStatus.OK.value());\r
+                //response.setContentType("application/json;charset=UTF-8");\r
+                //response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, e.getMessage())));\r
+                //response.sendError(HttpStatus.UNAUTHORIZED.value(),e.getMessage());\r
+                myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);\r
+                return;\r
+            }\r
+        }\r
+        filterChain.doFilter(request, response);\r
+    }\r
 \r
-       @Override\r
-       protected void doFilterInternal(HttpServletRequest request,\r
-                                                                                                                                       HttpServletResponse response, FilterChain filterChain)\r
-                       throws ServletException, IOException {\r
-               if (StringUtil.equals("/login/form", request.getRequestURI())\r
-                               && StringUtil.equalsIgnoreCase(request.getMethod(), "post")) {\r
-                       try {\r
-                               validate(request);\r
-                       } catch (ValidateCodeException e) {\r
-                               //response.setTransStatus(HttpStatus.OK.value());\r
-                               //response.setContentType("application/json;charset=UTF-8");\r
-                               //response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, e.getMessage())));\r
-                               //response.sendError(HttpStatus.UNAUTHORIZED.value(),e.getMessage());\r
-                               myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);\r
-                               return;\r
-                       }\r
-               }\r
-               filterChain.doFilter(request, response);\r
-       }\r
-\r
-       private void validate(HttpServletRequest request) throws ValidateCodeException {\r
-               VerifyCode imageCode = (VerifyCode) request.getSession().getAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
-               String inputCode;\r
-               try {\r
-                       inputCode = request.getParameter("imageCode");\r
-               } catch (Exception e) {\r
-                       throw new ValidateCodeException("获取验证码的值失败");\r
-               }\r
-               if (StringUtil.isEmpty(inputCode)) {\r
-                       throw new ValidateCodeException("验证码不能为空");\r
-               }\r
-               if (null == imageCode) {\r
-                       throw new ValidateCodeException("验证码不存在");\r
-               }\r
-               if (imageCode.isExpired()) {\r
-                       request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
-                       throw new ValidateCodeException("验证码已过期");\r
-               }\r
-               if (!StringUtil.equalsIgnoreCase(imageCode.getText(), inputCode)) {\r
-                       throw new ValidateCodeException("验证码不匹配");\r
-               }\r
-               request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
-       }\r
+    private void validate(HttpServletRequest request) throws ValidateCodeException {\r
+        VerifyCode imageCode = (VerifyCode) request.getSession().getAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
+        String inputCode;\r
+        try {\r
+            inputCode = request.getParameter("imageCode");\r
+        } catch (Exception e) {\r
+            throw new ValidateCodeException("获取验证码的值失败");\r
+        }\r
+        if (StringUtil.isEmpty(inputCode)) {\r
+            throw new ValidateCodeException("验证码不能为空");\r
+        }\r
+        if (null == imageCode) {\r
+            throw new ValidateCodeException("验证码不存在");\r
+        }\r
+        if (imageCode.isExpired()) {\r
+            request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
+            throw new ValidateCodeException("验证码已过期");\r
+        }\r
+        if (!StringUtil.equalsIgnoreCase(imageCode.getText(), inputCode)) {\r
+            throw new ValidateCodeException("验证码不匹配");\r
+        }\r
+        request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
+    }\r
 \r
 }\r
index 38a3fbe..3371b31 100644 (file)
@@ -2,6 +2,7 @@ package com.supwisdom.dlpay.framework.redisrepo;
 
 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 (file)
index 0000000..e1b5869
--- /dev/null
@@ -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 (file)
index 0000000..a39db67
--- /dev/null
@@ -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 (file)
index 0000000..5261586
--- /dev/null
@@ -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 (file)
index 0000000..da95e4f
--- /dev/null
@@ -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 (file)
index 0000000..67d6243
--- /dev/null
@@ -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 (file)
index 0000000..c01e430
--- /dev/null
@@ -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 (file)
index 0000000..6d10c46
--- /dev/null
@@ -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 (file)
index 0000000..fec5155
--- /dev/null
@@ -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 (file)
index 0000000..9f16334
--- /dev/null
@@ -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
index 514b80c..0914da6 100644 (file)
@@ -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 @@ class ApiJwtAuthenticationFilter : OncePerRequestFilter() {
             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 @@ class ApiJwtAuthenticationFilter : OncePerRequestFilter() {
                     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 @@ class ApiJwtAuthenticationFilter : OncePerRequestFilter() {
                 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 @@ class WebSecurityConfig {
                 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 @@ class WebSecurityConfig {
             @Autowired
             lateinit var validateCodeSecurityConfig: ValidateCodeSecurityConfig
             @Autowired
-            lateinit var authenticationFailureHandler: AuthenticationFailureHandler
+            lateinit var authenticationFailureHandler: MyAuthenticationFailureHandler
             @Autowired
             lateinit var passwordBCryptConfig: PasswordBCryptConfig
 
@@ -220,7 +285,7 @@ class WebSecurityConfig {
                 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")