您的位置:首页 > 运维架构 > Apache

将 Apache Shiro 改造成 JWT 认证方式

2017-09-30 00:38 411 查看

要想成功改造,只需做到三点

1.用 Shiro 提供的 DefaultWebSessionManager 替代默认的 ServletContainerSessionManager

- ServletContainerSessionManager 是 DefaultWebSecurityManager 使用的默认实现,用于 Web 环境,其直接使用 Servlet 容器的会话

- 因为我们无法控制服务器(例如 Tomcat)的会话管理,所以要自己维护会话池,即使用 DefaultWebSessionManager

2.自定义 SessionIdCookie 模板,其默认从 Cookie 读取 SessionId,但我们需要从请求头部读取 Token 字段

3.自定义 SessionIdGenerator,默认的 JavaUuidSessionIdGenerator 产生一个随机的 UUID 值来做 SessionId,但我们需要用 jwt 串作 SessionId

shiro.ini

[main]
#自定义会话 Cookie 模板,重写方法,使得从请求头部或参数读取 SessionId,而非 Cookie
sessionIdCookie=com.pomer.test.servlet.MySimpleCookie
#使用 Shiro 提供的 DefaultWebSessionManager,其维护自己的会话池,从而废弃服务器的会话池
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionIdCookie.name=Token
#sessionIdCookie.domain=sishuok.com
#sessionIdCookie.path=
#sessionIdCookie.maxAge=1800
sessionIdCookie.httpOnly=true
sessionManager.sessionIdCookie=$sessionIdCookie
sessionManager.sessionIdCookieEnabled=true
#自定义会话 ID 生成器(SessionId 返回 JWT 串,而非随机 UUID)
sessionIdGenerator=com.pomer.test.servlet.MySessionIdGenerator
#如果考虑分布式服务器,可以自定义数据库存储会话 Dao,这里使用了内存存取会话
sessionDAO=org.apache.shiro.session.mgt.eis.MemorySessionDAO
sessionDAO.sessionIdGenerator=$sessionIdGenerator
sessionManager.sessionDAO=$sessionDAO
securityManager.sessionManager=$sessionManager

#以下为 JWT 无关配置,请忽略

#声明 JdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled=true
jdbcRealm.authenticationQuery=SELECT password FROM user WHERE username = ?
jdbcRealm.userRolesQuery=SELECT r.name FROM role r JOIN user u ON r.id = u.role_id WHERE u.username = ?
jdbcRealm.permissionsQuery=SELECT a.name FROM authority a JOIN link_role_authority l ON a.id = l.authority_id JOIN role r ON r.id = l.role_id WHERE r.name = ?
#设置数据库
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/web?useSSL=false
dataSource.username=web
dataSource.password=123456
jdbcRealm.dataSource=$dataSource
#指定 securityManager 的 realms 实现
securityManager.realms=$jdbcRealm

#设置 url
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized

[urls]
#无需用户认证
/login=anon
/logout=anon
/unauthorized=anon
/static/**=anon
#需要用户认证
/authenticated=authc
#用户认证 + 角色验证
/role=authc,roles[admin]
#用户认证 + 权限验证
/permission=authc,perms["user_add"]


MySimpleCookie

public class MySimpleCookie extends SimpleCookie {

@Override
public String readValue(HttpServletRequest request, HttpServletResponse ignored) {
//读取 SessionId,即 jwt 串
String value = request.getHeader(getName());
if (value == null)
value = request.getParameter(getName());
return value;
}
}


MySessionIdGenerator

public class MySessionIdGenerator extends JavaUuidSessionIdGenerator {

static int count = 0;

@Override
public Serializable generateId(Session session) {
//如果进行了基于 JWT 的用户认证,可以获取加密生成的 JWT 串并返回,<JWT, Session> 存入 Shiro 会话池
// SessionId 即为 JWT 串,可通过 JWT 从会话池中获取相应的会话信息
if (++count % 2 == 0)
return String.valueOf(count);

//如果不存在用户认证,随便返回一个值,但不能为 null
return super.generateId(session);
}
}


index.jsp

<%@ page import="org.apache.shiro.SecurityUtils" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>

<%
System.out.println("会话id:" + SecurityUtils.getSubject().getSession().getId());
System.out.println("会话创建时间:" + SecurityUtils.getSubject().getSession().getStartTimestamp());
%>

</body>
</html>


启动服务器,当不提供任何参数时

//可以看到不断生成新的会话
会话id:ff70105d-b192-41aa-8b66-b1c5ff0e30fd
会话创建时间:Sat Sep 30 00:16:45 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:7228b82d-2520-4830-bb4f-d4cf450fe982
会话创建时间:Sat Sep 30 00:16:56 CST 2017
会话id:4
会话创建时间:Sat Sep 30 00:16:56 CST 2017
会话id:45f96994-3647-4aa1-8d67-a82c436c349f
会话创建时间:Sat Sep 30 00:16:56 CST 2017
会话id:6
会话创建时间:Sat Sep 30 00:16:56 CST 2017
会话id:a3eea41d-311d-407b-9e5d-7491eaf229b4
会话创建时间:Sat Sep 30 00:16:57 CST 2017
会话id:8
会话创建时间:Sat Sep 30 00:16:57 CST 2017
会话id:3f45c24f-a1ab-462c-984d-c32060213bf8
会话创建时间:Sat Sep 30 00:16:57 CST 2017
会话id:10
会话创建时间:Sat Sep 30 00:16:57 CST 2017


提供参数,将 URL 改为 http://localhost:8080/shiro/?token=2

//可以看到会话得到维持
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
会话id:2
会话创建时间:Sat Sep 30 00:16:46 CST 2017
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: