Authenticate restful api with Vert.x shiro jdbcRealm

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>

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326445140&siteId=291194637