整体采用SaToken框架
写登录系统,需要处理以下问题:
- 后端操作数据库
- token的设置
- 后端异常拦截
- 前端登陆页面
- 前端request处理
首先,我们先写后端操作数据库部分:
我们采用DDD框架,将后端结构分为apis, application, domain, infrastructure四个分层。
先来写domain层部分:
在domain层(领域层),建立领域实体类Account和相关映射。
1 2 3 4 5 6 7 8 9 10 11 12
| @TableName(value = "account", autoResultMap = true) class Account { var userId: String = "" var userName: String = "" var role: String = "" var password: String = "" var deleted: Int = 0 }
@Mapper interface AccountMapper: BaseMapper<Account>
|
这里一定记住mysql用_分开定义字段,而kotlin是用驼峰法定义字段。
在建立映射后,写领域服务AccountRepository:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Repository class AccountRepository(val accountMapper: AccountMapper) { fun selectAccountByName(userName: String): Account? { return accountMapper.selectOne( QueryWrapper<Account>().eq("user_name", userName) ) }
fun selectAccountById(userId: String): Account? { return accountMapper.selectOne( QueryWrapper<Account>().eq("user_id", userId) ) }
fun insertAccount(account: Account): Int { return accountMapper.insert(account) }
fun checkingUserName(userName: String): Boolean { return accountMapper.exists( QueryWrapper<Account>().eq("user_name", userName) ) } }
|
这里写了四个方法,用来登录时查询账号,登陆后查询账号,注册账号,查重用户名。
这样domain层部分就写好啦~
开始写后端异常处理器部分
在后端处理时,发生异常后,向前端抛出,前端也进行处理,完成异常的显示。
后端异常拦截部分:
1 2 3 4 5 6 7 8 9 10 11
| @RestControllerAdvice class GlobalExceptionHandler { @ExceptionHandler fun handlerException(exception: Exception): SaResult? { if ( exception is NotLoginException ) { return SaResult.code(501) } return SaResult.error(exception.message) } }
|
注意,默认返回code为500,不登录返回code为501,服务器寄了返回code为502
现在,前端Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| export const request: RequestConfig = { timeout: 20000, errorConfig: { errorHandler: (error) => { if (error.code === 'ERR_BAD_RESPONSE') { message.error('服务器寄了'); return; } message.error(error.message); }, }, responseInterceptors: [ (response) => { const { data: { code, msg }, } = response; switch (code) { case 500: throw new Error(msg); case 501: message.error('未登录账号'); history.push('/login'); break; } return response; }, ], };
|
errorHandler用来拦截异常,responseInterceptors来监视Response。
nginx的502报错直接发给errorHandler
现在,该写token部分了:
我们采用SaToken框架,这个嘎嘎好用√
只要将SaTokenConfigure类暴露在容器中,就可以完成配置啦:
1 2 3 4 5 6 7
| @Component class SaTokenConfigure: WebMvcConfigurer { override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(SaAnnotationInterceptor()) .addPathPatterns("/**") } }
|
在这里,我们启动了注解功能,可以校验是否登录。
然后再来写application层(应用层)与apis(接口层):
建立LoginService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Service class LoginService { @Autowired private lateinit var accountRepository: AccountRepository
fun register(userName: String, password: String) { val timestamp = Instant.now() var account = Account() account.userId = timestamp.toEpochMilli().toString() + (100..999).random().toString() account.userName = userName if ( accountRepository.checkingUserName(userName) ) { throw Exception("用户名重复") } account.password = BCrypt.hashpw(password, BCrypt.gensalt()) account.role = "user" account.deleted = 0 try { accountRepository.insertAccount(account) } catch (e: Exception) { throw Exception("注册失败") } }
fun login(userName: String, password: String) { val user = accountRepository.selectAccountByName(userName) ?: throw Exception("未查找到该用户") if (BCrypt.checkpw(password, user.password)) { StpUtil.login(user.userId) StpUtil.getSession().set("user", user) } else { throw Exception("密码错误") } }
fun logout() { StpUtil.logout() } }
|
- 先来说注册方法,我们采用时间戳来作id(因为没用户,时间戳就够了XD),然后查重用户名,重要的是
将前端发来的密码加密,采用BCrypt类提供的方法加密存入数据,最后将数据插入数据库就好啦。
建立LoginController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @RestController class LoginController { @Autowired private lateinit var loginService: LoginService
data class LoginReq( var userName: String, var password: String )
@PostMapping("/register") fun signIn(@RequestBody req: LoginReq) { loginService.register(req.userName, req.password) }
@PostMapping("/login") fun loginIn(@RequestBody req: LoginReq) { loginService.login(req.userName, req.password) }
@SaCheckLogin @PostMapping("/logout") fun loginOut(): String { loginService.logout() return "登出" }
@PostMapping("/isLogin") fun isLogin(): String { return if (loginService.isLogin()) "在" else "不在" }
}
|
这里提供四个api,注册,登录,退出,判断在线。接口层主要关注传来参数与交付给应用层,所以这里直接调用应用层就好啦
咳咳,一页写不下前端页面部分了,就不写了XD,大致就是写个页面加服务就行啦!
这样比较简陋的论坛进出系统就写好了。