This article aims to use Vert.x, shiro JdbcRealm to develop a demo that authenticates restfu api
Vert.x: see http://vertx.io
shiro: Visit http://shiro.apache.org/
The business logic is very simple, which is to implement user login verification, and then authenticate the restful api.
The database uses mysql.
Database name: myshiro
data sheet:
-- ---------------------------- -- Table structure for t_permission -- ---------------------------- DROP TABLE IF EXISTS `t_permission`; CREATE TABLE `t_permission` ( `id` int(11) NOT NULL, `permission` varchar(255) NOT NULL, `role_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_role -- ---------------------------- DROP TABLE IF EXISTS `t_role`; CREATE TABLE `t_role` ( `id` int(11) NOT NULL, `role_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_user -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` int(11) NOT NULL, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_user_role`; CREATE TABLE `t_user_role` ( `id` int(11) NOT NULL, `user_id` int(11) DEFAULT NULL, `role_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Background code:
package com.wof.realtime.apigateway; import java.util.HashSet; import java.util.Set; import org.apache.shiro.realm.jdbc.JdbcRealm; import org.apache.tomcat.jdbc.pool.DataSource; import io.vertx.core.AbstractVerticle; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.json.JsonObject; import io.vertx.ext.auth.AuthProvider; import io.vertx.ext.auth.User; import io.vertx.ext.auth.shiro.ShiroAuth; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.Session; import io.vertx.ext.web.handler.AuthHandler; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.CookieHandler; import io.vertx.ext.web.handler.RedirectAuthHandler; import io.vertx.ext.web.handler.SessionHandler; import io.vertx.ext.web.handler.UserSessionHandler; import io.vertx.ext.web.sstore.LocalSessionStore; public class ApiGatewayVerticle2 extends AbstractVerticle { private AuthProvider authProvider; @Override public void start(Future<Void> startFuture) throws Exception { // User permission information - JDBC form JdbcRealm jdbcRealm = getJdbcRealm(); authProvider = ShiroAuth.create(vertx, jdbcRealm); // router Router router = Router.router(vertx); // create session handler for all routes router.route().handler(BodyHandler.create()); router.route().handler(CookieHandler.create()); router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)).setSessionTimeout(1000 * 60 * 1)); router.route().handler(UserSessionHandler.create(authProvider)); // When there is no user session in the request, automatically jump to /login AuthHandler authHandler = RedirectAuthHandler.create(authProvider, "/login"); Set<String> authorities = new HashSet<String>(); authHandler.addAuthorities(authorities); // Install authHandler for all routes that require authentication router.route("/").handler(authHandler); router.route("/api/*").handler(authHandler); // restful api authentication router.get("/api/liaota/liaota").handler(this::listLiaotaHandler); router.put("/api/liaota/liaota/:id").handler(this::updateLiaotaHandler); router.post("/api/liaota/liaota/").handler(this::addLiaotaHandler); router.delete("/api/liaota/liaota/:id").handler(this::deleteLiaotaHandler); // Login jump, login verification, logout processing handler router.get("/login").handler(this::loginHandler); router.post("/login-auth").handler(this::loginAuthHandler); router.get("/logout").handler(context -> { context.clearUser(); context.response().setStatusCode(302).putHeader("Location", "/").end(); }); // start httpServer vertx.createHttpServer().requestHandler(router::accept).listen(8080, h-> { if(h.succeeded()) System.out.println("server start."); else h.cause().printStackTrace(); }); } /** * Get users, roles, permissions through JDBC * * @return */ private JdbcRealm getJdbcRealm(){ // The database connection pool is hard-coded here (the production environment uses the configuration file method) DataSource dataSource = new DataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/myshiro?useUnicode=true&characterEncoding=utf8"); dataSource.setUsername("demo"); dataSource.setPassword("123456"); // Configure the database to automatically connect after disconnection dataSource.setLogAbandoned(true); dataSource.setRemoveAbandoned(true); dataSource.setRemoveAbandonedTimeout(60); dataSource.setTestWhileIdle(true); dataSource.setValidationQuery("select id from user where name='demo'"); // Configure jdbcRealm JdbcRealm jdbcRealm = new JdbcRealm(); jdbcRealm.setDataSource(dataSource); jdbcRealm.setPermissionsLookupEnabled(true);//true: Allows the permissions to look up roles. false: only look up users and roles, not the permissions of roles. // jdbcRealm.setAuthenticationCachingEnabled(false);//Disable caching of user query results. After disabling, query from database every time. // jdbcRealm.setAuthorizationCachingEnabled(false);//Prohibit caching of roles and permission query results. After disabling, query from database every time. jdbcRealm.setCachingEnabled(false);//Disable caching // Modify the query database SQL and modify it according to your own database table structure. jdbcRealm.setAuthenticationQuery("select password from t_user where username = ?"); jdbcRealm.setUserRolesQuery("select t_r.role_name from t_user_role t_ur " + "inner join t_role t_r on t_ur.role_id=t_r.id " + "inner join t_user t_u on t_u.id = t_ur.user_id where t_u.username = ?"); jdbcRealm.setPermissionsQuery("select permission from t_permission t_p " + "inner join t_role t_r on t_r.id = t_p.role_id where t_r.role_name = ?"); return jdbcRealm; } private void loginAuthHandler(RoutingContext context) { HttpServerRequest req = context.request(); MultiMap params = req.formAttributes(); String username = params.get("username"); String password = params.get("password"); Session session = context.session(); JsonObject authInfo = new JsonObject().put("username", username).put("password", password); authProvider.authenticate(authInfo, res -> { JsonObject json = new JsonObject(); json.put("message", "loginFail"); if (res.succeeded()) { json.put("message", "loginSuccess"); User user = res.result(); context.setUser(user); if (session != null) { session.regenerateId(); // 更新session id } } req.response().headers().set("Content-Type", "text/html; charset=UTF-8"); req.response().end(json.encode()); }); } private void loginHandler(RoutingContext context) { HttpServerRequest req = context.request(); req.response().headers().set("Content-Type", "text/html; charset=UTF-8"); req.response().end("login"); } private void listLiaotaHandler(RoutingContext context) { context.user().isAuthorised("query", h -> { if(h.result()) doSomething(context); else { authFail(context); } }); } private void updateLiaotaHandler(RoutingContext context) { context.user().isAuthorised("update", h -> { if(h.result()) doSomething(context); else { authFail(context); } }); } private void addLiaotaHandler(RoutingContext context) { context.user().isAuthorised("add", h -> { if(h.result()) doSomething(context); else { authFail(context); } }); } private void deleteLiaotaHandler(RoutingContext context) { context.user().isAuthorised("delete", h -> { if(h.result()) doSomething(context); else { authFail(context); } }); } private void doSomething(RoutingContext context){ System.out.println("Authentication passed, business logic processing."); JsonObject json = new JsonObject(); json.put("success", true).put("message", "Business processing completed."); context.request().response().headers().set("Content-Type", "text/html; charset=UTF-8"); context.request().response().end(json.toString()); } private void authFail(RoutingContext context){ JsonObject json = new JsonObject(); json.put("success", false).put("message", "无此权限。"); context.request().response().headers().set("Content-Type", "text/html; charset=UTF-8"); context.request().response().end(json.toString()); } }
pom.xml needs to import:
<dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-core</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-auth-shiro</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> <version>8.5.11</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-juli</artifactId> <version>8.5.11</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> </dependencies>