用Vert.x shiro jdbcRealm对restful api鉴权

 

本文旨在用Vert.x,shiro JdbcRealm开发一个对restfu api进行鉴权的demo

Vert.x:参看 http://vertx.io

shiro:参看 http://shiro.apache.org/

业务逻辑很简单,就是实现用户登录验证,然后对restful api进行鉴权。

数据库用mysql。

数据库名:myshiro

数据表:

 

 1 -- ----------------------------
 2 -- Table structure for t_permission
 3 -- ----------------------------
 4 DROP TABLE IF EXISTS `t_permission`;
 5 CREATE TABLE `t_permission` (
 6   `id` int(11) NOT NULL,
 7   `permission` varchar(255) NOT NULL,
 8   `role_id` int(11) DEFAULT NULL,
 9   PRIMARY KEY (`id`)
10 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
11  
12 -- ----------------------------
13 -- Table structure for t_role
14 -- ----------------------------
15 DROP TABLE IF EXISTS `t_role`;
16 CREATE TABLE `t_role` (
17   `id` int(11) NOT NULL,
18   `role_name` varchar(255) DEFAULT NULL,
19   PRIMARY KEY (`id`)
20 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
21  
22 -- ----------------------------
23 -- Table structure for t_user
24 -- ----------------------------
25 DROP TABLE IF EXISTS `t_user`;
26 CREATE TABLE `t_user` (
27   `id` int(11) NOT NULL,
28   `username` varchar(255) DEFAULT NULL,
29   `password` varchar(255) DEFAULT NULL,
30   PRIMARY KEY (`id`)
31 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
32  
33 -- ----------------------------
34 -- Table structure for t_user_role
35 -- ----------------------------
36 DROP TABLE IF EXISTS `t_user_role`;
37 CREATE TABLE `t_user_role` (
38   `id` int(11) NOT NULL,
39   `user_id` int(11) DEFAULT NULL,
40   `role_id` int(11) DEFAULT NULL,
41   PRIMARY KEY (`id`)
42 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
43  后台代码:
44 
45  

 

  1 package cn.endv.realtime.apigateway;
  2  
  3 import java.util.HashSet;
  4 import java.util.Set;
  5  
  6 import org.apache.shiro.realm.jdbc.JdbcRealm;
  7 import org.apache.tomcat.jdbc.pool.DataSource;
  8  
  9 import io.vertx.core.AbstractVerticle;
 10 import io.vertx.core.Future;
 11 import io.vertx.core.MultiMap;
 12 import io.vertx.core.http.HttpServer;
 13 import io.vertx.core.http.HttpServerRequest;
 14 import io.vertx.core.json.JsonObject;
 15 import io.vertx.ext.auth.AuthProvider;
 16 import io.vertx.ext.auth.User;
 17 import io.vertx.ext.auth.shiro.ShiroAuth;
 18 import io.vertx.ext.web.Router;
 19 import io.vertx.ext.web.RoutingContext;
 20 import io.vertx.ext.web.Session;
 21 import io.vertx.ext.web.handler.AuthHandler;
 22 import io.vertx.ext.web.handler.BodyHandler;
 23 import io.vertx.ext.web.handler.CookieHandler;
 24 import io.vertx.ext.web.handler.RedirectAuthHandler;
 25 import io.vertx.ext.web.handler.SessionHandler;
 26 import io.vertx.ext.web.handler.UserSessionHandler;
 27 import io.vertx.ext.web.sstore.LocalSessionStore;
 28  
 29 public class ApiGatewayVerticle2 extends AbstractVerticle {
 30     
 31     private AuthProvider authProvider;
 32     
 33     @Override
 34     public void start(Future<Void> startFuture) throws Exception {
 35         
 36         // 用户权限信息-JDBC形式        
 37         JdbcRealm jdbcRealm = getJdbcRealm();
 38         authProvider = ShiroAuth.create(vertx, jdbcRealm);
 39         
 40         // 路由器
 41         Router router = Router.router(vertx);
 42         
 43         // 为所有route创建session handler
 44         router.route().handler(BodyHandler.create());
 45         router.route().handler(CookieHandler.create());
 46         router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)).setSessionTimeout(1000 * 60 * 1));            
 47         router.route().handler(UserSessionHandler.create(authProvider));
 48         
 49         // 当请求中没有user session时,自动跳转到 /login
 50         AuthHandler authHandler = RedirectAuthHandler.create(authProvider, "/login"); 
 51         Set<String> authorities = new HashSet<String>();
 52         authHandler.addAuthorities(authorities);
 53  
 54         // 为所有需要鉴权的路由安装authHandler
 55         router.route("/").handler(authHandler);
 56         router.route("/api/*").handler(authHandler);
 57         
 58         // restful api 鉴权
 59         router.get("/api/liaota/liaota").handler(this::listLiaotaHandler);
 60         router.put("/api/liaota/liaota/:id").handler(this::updateLiaotaHandler);
 61         router.post("/api/liaota/liaota/").handler(this::addLiaotaHandler);
 62         router.delete("/api/liaota/liaota/:id").handler(this::deleteLiaotaHandler);
 63                     
 64         // 登录跳转、登录验证、登出处理handler
 65         router.get("/login").handler(this::loginHandler);
 66         router.post("/login-auth").handler(this::loginAuthHandler);
 67         router.get("/logout").handler(context -> {
 68             context.clearUser();
 69             context.response().setStatusCode(302).putHeader("Location", "/").end();
 70         });                    
 71         
 72         // 启动httpServer
 73         vertx.createHttpServer().requestHandler(router::accept).listen(8080, h-> {
 74             if(h.succeeded())
 75                 System.out.println("server start.");
 76             else 
 77                 h.cause().printStackTrace();
 78         });
 79     }
 80     
 81     /**
 82      * 通过JDBC获取用户、角色、权限
 83      * 
 84      * @return
 85      */
 86     private JdbcRealm getJdbcRealm(){
 87         // 数据库连接池 此处用硬编码方式(生产环境用配置文件方式)
 88         DataSource dataSource = new DataSource();
 89         dataSource.setDriverClassName("com.mysql.jdbc.Driver");
 90         dataSource.setUrl("jdbc:mysql://localhost:3306/myshiro?useUnicode=true&amp;characterEncoding=utf8");
 91         dataSource.setUsername("demo");
 92         dataSource.setPassword("123456");
 93         // 配置数据库断开后自动连接
 94         dataSource.setLogAbandoned(true);
 95         dataSource.setRemoveAbandoned(true);
 96         dataSource.setRemoveAbandonedTimeout(60);
 97         dataSource.setTestWhileIdle(true);
 98         dataSource.setValidationQuery("select id from user where name='demo'");
 99         
100         // 配置jdbcRealm
101         JdbcRealm jdbcRealm = new JdbcRealm();
102         jdbcRealm.setDataSource(dataSource);
103         jdbcRealm.setPermissionsLookupEnabled(true);//true:允许查找角色的权限。false:只查找用户和角色,不会查找角色的权限。
104         
105 //        jdbcRealm.setAuthenticationCachingEnabled(false);//禁止缓存用户查询结果。禁止后,每次都要从数据库查询。
106 //        jdbcRealm.setAuthorizationCachingEnabled(false);//禁止缓存角色,权限查询结果。禁止后,每次都要从数据库查询。
107         jdbcRealm.setCachingEnabled(false);//禁止缓存
108         
109         // 修改查询数据库SQL,根据自己的数据库表结构进行修改。
110         jdbcRealm.setAuthenticationQuery("select password from t_user where username = ?");
111         jdbcRealm.setUserRolesQuery("select t_r.role_name from t_user_role t_ur "
112                 + "inner join t_role t_r on t_ur.role_id=t_r.id  "
113                 + "inner join t_user t_u on t_u.id = t_ur.user_id where t_u.username = ?");
114         jdbcRealm.setPermissionsQuery("select permission from t_permission t_p "
115                 + "inner join t_role t_r on t_r.id = t_p.role_id where t_r.role_name = ?");    
116         
117         return jdbcRealm;
118     }
119     
120     private void loginAuthHandler(RoutingContext context) {
121         HttpServerRequest req = context.request();
122         MultiMap params = req.formAttributes();
123         String username = params.get("username");
124         String password = params.get("password");
125  
126         Session session = context.session();
127         JsonObject authInfo = new JsonObject().put("username", username).put("password", password);
128         
129         authProvider.authenticate(authInfo, res -> {
130             JsonObject json = new JsonObject();
131             json.put("message", "loginFail");
132     
133             if (res.succeeded()) {
134                 json.put("message", "loginSuccess");
135                 User user = res.result();
136                 context.setUser(user);
137                 if (session != null) {
138                     session.regenerateId(); // 更新session id    
139                 }
140             }
141             req.response().headers().set("Content-Type", "text/html; charset=UTF-8");
142             req.response().end(json.encode());
143         });
144     }
145     
146     private void loginHandler(RoutingContext context) {
147         HttpServerRequest req = context.request();
148         req.response().headers().set("Content-Type", "text/html; charset=UTF-8");
149         req.response().end("login");
150     }
151     
152     private void listLiaotaHandler(RoutingContext context) {
153         context.user().isAuthorised("query", h -> {
154             if(h.result())
155                 doSomething(context);
156             else {
157                 authFail(context);
158             }
159         });
160     }
161     
162     private void updateLiaotaHandler(RoutingContext context) {
163         context.user().isAuthorised("update", h -> {
164             if(h.result())
165                 doSomething(context);
166             else {
167                 authFail(context);
168             }
169         });
170     }
171     
172     private void addLiaotaHandler(RoutingContext context) {
173         context.user().isAuthorised("add", h -> {
174             if(h.result())
175                 doSomething(context);
176             else {
177                 authFail(context);
178             }
179         });
180     }
181     
182     private void deleteLiaotaHandler(RoutingContext context) {
183         context.user().isAuthorised("delete", h -> {
184             if(h.result())
185                 doSomething(context);
186             else {
187                 authFail(context);
188             }
189         });
190     }
191     
192     private void doSomething(RoutingContext context){
193         System.out.println("鉴权通过,进行业务逻辑处理。");
194         JsonObject json = new JsonObject();
195         json.put("success", true).put("message", "业务处理完成。");
196         context.request().response().headers().set("Content-Type", "text/html; charset=UTF-8");
197         context.request().response().end(json.toString());
198     }
199     
200     private void authFail(RoutingContext context){
201         JsonObject json = new JsonObject();
202         json.put("success", false).put("message", "无此权限。");
203         context.request().response().headers().set("Content-Type", "text/html; charset=UTF-8");
204         context.request().response().end(json.toString());
205     }
206  
207 }

pom.xml需要引入:

 1     <dependencies>
 2         <dependency>
 3             <groupId>io.vertx</groupId>
 4             <artifactId>vertx-core</artifactId>
 5             <version>3.4.2</version>
 6         </dependency>
 7         <dependency>
 8             <groupId>io.vertx</groupId>
 9             <artifactId>vertx-web</artifactId>
10             <version>3.4.2</version>
11         </dependency>
12         <dependency>
13             <groupId>io.vertx</groupId>
14             <artifactId>vertx-auth-shiro</artifactId>
15             <version>3.4.2</version>
16         </dependency>
17         <dependency>
18             <groupId>org.apache.tomcat</groupId>
19             <artifactId>tomcat-jdbc</artifactId>
20             <version>8.5.11</version>
21         </dependency>
22         <dependency>
23             <groupId>org.apache.tomcat</groupId>
24             <artifactId>tomcat-juli</artifactId>
25             <version>8.5.11</version>
26         </dependency>
27         <dependency>
28             <groupId>mysql</groupId>
29             <artifactId>mysql-connector-java</artifactId>
30             <version>5.1.26</version>
31         </dependency>
32     </dependencies>

 

上一篇:test


下一篇:非常实用的 Python 库,推一次火一次