2. 设计意图
应用启动
package fun.mortnon;
import io.micronaut.runtime.Micronaut;
/**
* 应用启动类
*
* @author dev2007
* @date 2023/2/3
*/
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
Micronaut 应用的启动类与 Spring Boot 类似,只要找到 main
方法就找到了入口。
application.yml
项目的配置文件,也与 Spring Boot 类似,且支持多种格式,你可以变更为你喜欢的格式。
micronaut:
application:
name: mortnon-micronaut
server:
port: 8080
cors:
enabled: true
serde:
serialization:
# 属性不论是否为空值,都要序列化
inclusion: ALWAYS
security:
# 认证方式,bearer :JWT 以请求头形式传递;cookie:JWT 以 cookies 形式传递
authentication: cookie
# 头像 URL 不鉴权
intercept-url-map:
- pattern: ${AVATAR_URL/**:/profile/avatar/**}
http-method: GET
access:
- isAnonymous()
# 用户导入模板不鉴权
- pattern: /users/template/**
http-method: GET
access:
- isAnonymous()
redirect:
enabled: false
endpoints:
login:
enabled: false
logout:
enabled: false
token:
jwt:
bearer:
header-name: Authorization
prefix: Bearer
generator:
access-token:
expiration: 28800
signatures:
secret:
generator:
secret: GvnfNoDSgD22VTkDjUear6WNcf5g1Bwi7aQ9BwpvTFeGcpb8TJsQgbAh9T92kU2MPpSK2BIWVvNDDpYEGgyXzdpof6e9LtWtd30MujSUXawjNAsPk9ZJa7kdEgHh5LuqQVWxL6GVJOuMmeX2L0TZ5aOdslazChM9PaNeYzaxJ7mLonMwwA1N0O7pK5wnTt3LECZf6FFH13MuioFI4qEjhVfeEcs5hIzLt2POXE8fyjC0dPe9yuahKTEeqva99kwKbk9TJL7lDJRsLJSvWKUldxUJEKVBXeunFsP2DWpKsroxslJNuHow8NZeWLMAV5HELXwxgdGjSrfrWStRNwpa76yh78vuYm5QMBfmim7FBdX0L9uInN6kLXNLRdkBIdiA3Shg0vvnsHr8QKFebk1rcpk8XApf2lrE1awISyWEiSE3vcSGd6SAyDBozGCzpubIgQ17ialZYnoI3Roe3dEfOufNKqoLeqT7nvbsBKwf56PF577zxoqk0wXnvClfI8vpjv3ucCE7BVgczg9DkNwfwcG4Xr3ryB340qi3PmgRHUTk68c6vRpeOxTpNokfzWiFfwmAfjwsTBrZFQhrSSbE2ej3gOoGeJr2eVBLs4TXcZSkFa1Sb1Xt1OBxlK798JLGlQJcuSMT5DrbWw5lg6ou1BU1xvFvMQxV35E4a3Q3xjltCcab5zfvewOAazGly4oItigdSzHRyVyqjEhWfgqgUhC954zOzBlhzxLfwYeSWsm6FCyGLnaqSOndXhJ5ZgLSjXzwuHNOTjxSpiyyUqoyUewADAHS5Gvz8cMFXB7X4YrUWGHNqXSGjeFUat5Y7H7VPn2hb0oV2wS7ABFvccHBa1LemClyTUc5gTmuzLaAWPXJksLAOHaPLPY818j0a81wD0vcxkqwJjurKnScFqNUpv6bR54MZDstVLE78kZg5vtXFpZoT5bdmnQTv6miUpyDxVU3hfLLsBMpCDFN9g0AjpRgj8QFoo1kor8Ci7a0pWclJauJrvu1FY79imiSTHDhL9A7aLWnN4AfUzcNYHgvGKgJngMuKJC3TkkjYRQldBVmkrG3YzG3rdlNtsUhFi62ZHBn17Rx20L314PQ9CtX82ItEsmCaNM1vImyw21TcQVKIwxXVyZvOsSQq0jHBwBsWciwipNB9a39MqypP9iaskS4a6jRZSiQaWLyfaD3Rdv74JPGRBHnf0lj4hFfbuk5JE5q3lRI7obKtGVOGIDg6jnNctQxO3XfV3J4cuwhdkmX6I4sjCwlX4bcmQs0P75k
jws-algorithm: HS256
router:
static-resources:
# 头像文件映射的 URL
images:
enabled: true
paths:
- file:D:/uploadPath
mapping: ${AVATAR_URL/**:/profile/avatar/**}
# 用户导入模板映射的 URL
user:
enabled: true
paths:
- classpath:template
mapping: /users/template/**
netty:
default:
allocator:
max-order: 3
r2dbc:
datasources:
default:
driver: mysql
host: ${MYSQL_HOST:localhost}
port: ${MYSQL_PORT:3306}
username: ${MYSQL_USERNAME:root}
password: ${MYSQL_PASSWORD:123456}
database: mortnon
datasources:
default:
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/mortnon
username: ${MYSQL_USERNAME:root}
password: ${MYSQL_PASSWORD:123456}
driverClassName: com.mysql.cj.jdbc.Driver
flyway:
datasources:
default:
enabled: true
redis:
# 如果使用 sentinel,REDIS_SCHEME 为 redis_sentinel,直连 redis REDIS_SCHEME 为 redis
uri: ${REDIS_SCHEME:redis}://${REDIS_HOST:localhost}:${REDIS_PORT:6379}#mymaster
# 提供 Jackson 庫配置,支持特定处能使用 JacksonConfiguration
jackson:
propertyNamingStrategy: SNAKE_CASE
mortnon:
captcha:
# 验证码有效时长(秒)
expire-seconds: 600
jwt:
# 服务端是否记录 token ,默认为 true
consistency: true
common:
# 缓存存储类型。Local:内存存储,Redis:redis存储
storage-type: ${STORE_TYPE:Redis}
# 日志相关默认语言
lang: zh
# 为前端生成的 RSA 公钥有效时长(分钟)
rsa-ttl: 30
# 双因子码有效时长(秒)
double-factor-ttl: 60
# 公用的 RSA 公钥
public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCN74zEheRhsnCRzj9FOJJ5/zXez0Pmbu1pu21/lyA5VT3QH7xSXnBCOwhiYDtTn0Bg1OQYD2wBNajdfJ2tGW1gVYTiR6vOe5blwe+dtYK8h0uvnQQtTDZ3TqE8VnEvb1xXy9a2zERujsdPIpZAJCrdGcSB/i4Mg8mc91dlY+MTNQIDAQAB
# 公用的 RSA 私钥,用于非登录的敏感数据加密,与前端的公钥一对
secret: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI3vjMSF5GGycJHOP0U4knn/Nd7PQ+Zu7Wm7bX+XIDlVPdAfvFJecEI7CGJgO1OfQGDU5BgPbAE1qN18na0ZbWBVhOJHq857luXB7521gryHS6+dBC1MNndOoTxWcS9vXFfL1rbMRG6Ox08ilkAkKt0ZxIH+LgyDyZz3V2Vj4xM1AgMBAAECgYA6TcHMZJe5DhyxuEBDRgbdeFl/qjJhMQXzXduQAtto0bwvhynLyCFjA27hQlh8dDl0zV7ZB1S9dnZkbTE0DvLfcZYdd7WTXzYtFkHju1j4Pw5I6aOmimxxH8hHpTcpXvzWAIUIRDZv32wC3/GOWOn6mv9WeO5eCLrSNWiNOB5HbQJBAI87dinDyMaH2DuK3N4DjJL7DZr+4dCtQTYeZpLMqgbxrb4CP4SrieSTJv/1TJdFfS+NB8AFRoEg7E16i7RyulMCQQD9rsWhLCCN1lO1gyx00lS+4IwF6gU8kzW7dWw+iIK5jr4f8eFYGUSddl4D8pYDK2y+QXeie+UHYH78F4kp5RtXAkBfFLjnFqnEaM85D4B4/OAnffLVLlfW//wnId0znBtOisasOK/TsU/TZX/hMARM8vscQsGfTaE0/RcAg9YTzokzAkEAuxPJ9oOeNjsaXD8x56y8hk79s9bSqcs90tJTUwJEtSaJGvG7ZsarjRufVXXuHsRFW0DYQuDvEzOcMbgBlP253QJAV/f113AEzG/Av1fNF4fPnVcPPh9bs180eexnGSXHseF1894WKoZ3XbuwaYWo+ir8Wi+FVZuVRI6uMXPD2P44oA==
# 密码错误的检查周期(分钟)
check-duration: 3
主要配置及环境变量说明
application.yml
配置文件中,主要分为几块:
micronaut
:框架的主要配置,包括应用端口、认证相关配置、静态资源 URL 等。netty
:项目使用的 Web 服务器 netty 的配置。如果 Web 服务器改用 Tomcat,将使用其他配置。r2dbc
:响应式数据库的连接配置datasources
:非响应式、普通的数据库连接配置(用于flyway
)flyway
:数据库版本管理工具flyway
的配置redis
:NoSQL 中间件redis
的连接配置jackson
:Jackson 序列化工具的配置(用于特殊处理 Http 访问异常时的响应体)mortnon
:项目自定义的配置项,主要分为:captcha
:图形验证码配置jwt
:认证 Token 配置common
:通用配置
环境变量如下:
MICRONAUT_SERVER_PORT
:指定应用的端口(在应用启动时可用)MYSQL_HOST
:MySQL 数据库服务器 IP 或域名MYSQL_PORT
:MySQL 数据库服务器端口MYSQL_USERNAME
:MySQL 数据库服务器用户名MYSQL_PASSWORD
:MySQL 数据库服务器用户对应密码REDIS_HOST
:Redis 服务器 IP 或域名REDIS_PORT
:Redis 服务器端口STORE_TYPE
:应用缓存类型。Local
:内存缓存;Redis
:Redis 缓存。默认为Redis
。
登录认证
入口
项目的认证相关控制器位于:fun.mortnon.web.controller.auth
中,主要有:
AuthLoginController
:登录相关。继承自LoginController
。AuthLogoutController
:注销相关CaptchaController
:图形验证码相关
登录认证相关的 API 配置了 @Secured(SecurityRule.IS_ANONYMOUS)
,允许未登录方法,其他未配置该注解的 API 在未登录时会响应 401
,无权限访问的 API,会响应 403
。
认证逻辑
项目的认证由 Authenticator
对象自动维护,它将查找当前项目的 AuthenticationProvider
接口的实现类 AuthenticationProviderUserPassword
,在方法 authenticate
完成认证逻辑。
认证通过后,将会进入到 LoginHandler
的实现类中进行 Token 的生成、响应等逻辑,在应用中实现了两个处理器:
CookieAuthLoginHandler
JwtAuthLoginHandler
两个处理器,由 application.yml
中的配置决定使用哪个:
security:
# 认证方式,bearer :JWT 以请求头形式传递;cookie:JWT 以 cookies 形式传递
authentication: cookie
鉴权逻辑
认证通过后,对 API 访问的鉴权,由 MortnonTokenValidator
触发,它替换了框架自带的 JwtTokenValidator
,以校验 Token 的存在性:是否持久化存储了,如果是,当前存储中有无此 Token。
校验 Token 存在性通过后,将由框架遍历项目中的 SecurityRule
实现类进行鉴权,项目中实现了 PermissionRule
用于判定当前 Token 是否具有访问相应 API 的权限。
具体 API 对应权限值定义在数据库表:
sys_api
。权限定义在数据库表:sys_permission
。
项目中我们将 API 的控制放置到外部的数据库表,是为了减少控制与代码的耦合。这样灵活的设计在某䗰场景下,可以临时修改用户访问权限。
数据格式
在项目中,属性定义使用驼峰风格,转为 Json 时,使用小写加下划线风格。
在 Bean 定义中,使用如下注解:
@Data
@Serdeable(naming = SnakeCaseStrategy.class)
@Introspected
public class LogPageSearch
@Data
:Lombok 注解。自动生成Getter
和Setter
,可不用。@Serdeable(naming = SnakeCaseStrategy.class)
:框架 Serde 序列化注解。保证属性和 Json 格式转换符合我们的设计。如果不需要对应 Json 为小写下划线风格,仅声明@Serdeable
即可。@Introspected
:自省注解。保证对象在框架中转换正确。