基于Shiro的登录功能 设计思路
认证流程
Shiro的认证流程可以看作是个“有窗户黑盒”,
整个流程都有框架控制,对外的入口只有subject.login(token);,这代表“黑盒”
流程中的每一个组件,都可以使用Spring IOC容器里的Bean来替换,以实现拓展化、个性化,这代表“有窗户”。
本示例的认证流程可以参考下图:

(黑色区域中的红箭头线只代表受关注组件的流程,而不是直接调用,实际上Shiro每个流程的组件都是互相解耦的)
黑色区域里的两块白色区域,就是两个自定义的类。
关键代码如下:
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
AccountPo po = accountMapper.getByUsername(username);
if (po == null) {
throw new UnknownAccountException("用户名或密码错误。");
}
if (!po.getEnable_flag()) {
throw new DisabledAccountException("该用户已被禁用。");
}
CredentialsDto credentials = new CredentialsDto(po.getPassword(), po.getSalt());
return new SimpleAuthenticationInfo(username, credentials, getName());
}
/**
* 密码匹配
*/
@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo info) {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String password = new String(token.getPassword());
CredentialsDto credentials = (CredentialsDto) info.getCredentials();
String salt = credentials.getSalt();
String passwordMd5 = DigestUtils.md5Hex(password + salt);
boolean result = equals(passwordMd5, credentials.getPasswordMd5());
if (!result) {
throw new IncorrectCredentialsException("用户名或密码错误。");
}
return true;
}
认证过滤
直接在spring-shiro.xml的shiroFilter.filterChainDefinitions中定义哪些URL需要认证才能访问并不利于维护,
更合适的方法可能是写一个读取器,告诉filterChainDefinitions哪些URL不需要认证即可,需要认证的写在最后
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/" />
<property name="unauthorizedUrl" value="/html/unauthorized.html" />
<property name="filterChainDefinitions">
<value>
#{filterChainDefinitionsLoader.anonUrls()}
/**=authc
</value>
</property>
</bean>
/**
* Shiro的FilterChainDefinitions配置加载器
*
* @author Deolin
*/
public class FilterChainDefinitionsLoader { @Autowired
private UnauthenticationMapper unauthenticationMapper; /**
* 从数据库`unauthorization`表中读取所有匿名URL
*
* @return FilterChainDefinitions内容片段
*/
public String anonUrls() {
StringBuilder sb = new StringBuilder(400);
List<String> urls = authenticationMapper.listUrls();
for (String url : urls) {
sb.append(url);
sb.append("=anon");
sb.append(CRLF);
}
return sb.toString();
} }
FilterChainDefinitionsLoader.anonUrls()会在项目启动时调用,读取到所有匿名权限URL,
拼接成字符串,返回给filterChainDefinitions
授权配置
采用基于角色的权限设计,一个权限对应一个URL。一个用户间接地拥有多个权限,间接地能够访问多个权限所一一对应的URL。
这样一来,权限会全部配置在spring-shiro.xml的shiroFilter.filterChainDefinitions中,例如:
/html/students-view.html=perms["students-view"]
/student/add=perms["studentAdd"]
/student/list=perms["studentList"]
红蓝部分都是可自定义的,项目中每个涉及到权限的URL,都应该定义,
并保存到数据库`permission`表的name字段和url字段。
即`permission`是张字典表,项目中有多少个涉及到权限的URL,表中就会有多少字段。
授权配置也需要读取器
<property name="filterChainDefinitions">
<value>
#{filterChainDefinitionsLoader.anonUrls()}
#{filterChainDefinitionsLoader.permsUrls()}
/**=authc
</value>
</property>
/**
* 从数据库`permission`表中读取所有权限
*
* @return FilterChainDefinitions内容片段
*/
public String permsUrls() {
StringBuilder sb = new StringBuilder(400);
List<PermissionPo> pos = permissionMapper.list();
for (PermissionPo po : pos) {
sb.append(po.getUrl());
sb.append("=perms[\"");
sb.append(po.getName());
sb.append("\"]");
sb.append(CRLF);
}
return sb.toString();
}
授权流程

关键代码如下:
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roleNames = accountMapper.listRoleNames(username);
Set<String> permissionNames = accountMapper.listPermissionNames(username);
info.setRoles(roleNames);
info.setStringPermissions(permissionNames);
return info;
}
基于过滤器链的认证、权限检查
shiro的filterChainDefinitions是在项目启动是加载的,所以可以通过spel表达式,调用bean的方法,将各种字典表中的URL,拼接起来,返回给filterChainDefinitions,解释为“xxxxURL需要认证才能访问”“xxURL需要yyy的权限才能访问”。
这里需要事先设计好字典表。
自定义过滤器
shiro的自带过滤器,似乎无法处理AJAX请求,至少无法返回项目通用JSON数据结构,比如自带的org.apache.shiro.web.filter.authc.AuthenticationFilter类和org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter类(它们分别被过滤器链中的authc标识和perms标识所映射),它们各自的onAccessDenied方法,只用重定向处理,而没有返回JSON的处理,所以,需要专门写一些自定义过滤器,以及用于处理AJAX请求的工具类。
/**
* 为shiro的自定义Filter准备的工具类<br>
* <br>
* 用于判断请求是否是AJAX请求和输出JSON
*
* @author Deolin
*/
public class FilterUtil { public static boolean isAJAX(ServletRequest request) {
return "XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"));
} public static void out(ServletResponse response, RequestResult requestResult) {
ObjectMapper jackson = new ObjectMapper();
PrintWriter out = null;
try {
response.setCharacterEncoding("UTF-8");
out = response.getWriter();
out.println(jackson.writeValueAsString(requestResult));
} catch (Exception e) {} finally {
if (null != out) {
out.flush();
out.close();
}
}
} }
/**
* 自定义AccessControlFilter<br>
* <br>
* 这个类会在spring-shiro.xml注册为looseAuthc,作为filterChainDefinitions的一个自定义标记,未通过过滤的页面请求会重定向到登录页面,未通过过滤的AJAX请求会返回JSON
*
* @author Deolin
*/
public class LooseAuthcFilter extends AccessControlFilter { @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
Subject subject = getSubject(request, response);
// “记住我”自动登录 或 输入用户名+密码手动登录
if (subject.isRemembered() || subject.isAuthenticated()) {
return true;
} else {
return false;
}
} // 如果isAccessAllowed返回false,则调用这个方法
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (FilterUtil.isAJAX(request)) {
// 返回JSON对象,前端根据resp.result=-2跳转到登录页面
FilterUtil.out(response, RequestResult.unauthenticate());
} else {
// 重定向到登录页面
saveRequestAndRedirectToLogin(request, response);
}
return false;
} }
项目下载
https://github.com/spldeolin/login-demo
基于Shiro的登录功能 设计思路的更多相关文章
- 基于Redis的短链接设计思路
[Markdown阅读][1] 今天上班的时候收到一个需要短链接的需求,之前的做法都是使用了新浪的短链接API(https://api.weibo.com/2/short_url/shorten.js ...
- SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器 /** * cookie对象; * rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cooki ...
- 实现单点登录功能的思路以及kafka同步数据
单点登录以及用户数据同步思路与方案 当公司业务分布于多个子系统时, 同一用户在A系统注册,即可在其他所有关联系统使用, 并支持登录A系统后,自动在其他系统登录,退出同理. 在A平台修改通用的用户数据( ...
- 基于权限安全框架Shiro的登录验证功能实现
目前在企业级项目里做权限安全方面喜欢使用Apache开源的Shiro框架或者Spring框架的子框架Spring Security. Apache Shiro是一个强大且易用的Java安全框架,执行身 ...
- django BBS project login登录功能实现
1.models from django.db import models # Create your models here. from django.contrib.auth.models imp ...
- PHP实现登录功能DEMO
PHP实现登录的原理是什么呢?就是利用Session实现的,用户访问网站,系统会自动在服务器生成一个Session文件,这个Session可以用来存储用户的登录信息.好了,这是基本储备,我们下面来实现 ...
- 二十 Filter&自动登录功能
Filter过滤器 过滤器,其实就是对客户端发出来的请求进行过滤,浏览器发出,然后服务器用Servelt处理.在中间就可以过滤,起到的是拦截的作用. 不仅仅作用于客户端请求,而且过滤服务器响应 作用: ...
- 设计基于HTML5的APP登录功能及安全调用接口的方式
转自:http://blog.csdn.net/linlzk/article/details/45536065 最近发现群内大伙对用Hbuilder做的APP怎么做登录功能以及维护登录状态非常困惑,而 ...
- 设计基于HTML5的APP登录功能及安全调用接口的方式(原理篇)
登录 保存密码 安全 加密 最近发现群内大伙对用Hbuilder做的APP怎么做登录功能以及维护登录状态非常困惑,而我前一段时间正好稍微研究了一下,所以把我知道的告诉大家,节约大家查找资料的时间. 你 ...
随机推荐
- NodeJS入门--环境搭建 IntelliJ IDEA
NodeJS入门–环境搭建 IntelliJ IDEA 本人也刚开始学习NodeJS,所以以此做个笔记,欢迎大家提出意见. 1.首先 下载安装NodeJS,下载安装IntelliJ IDEA 2.接下 ...
- .Dot NET Cored简介
一.诞生原因 1..Net平台封闭. 2.不支持跨平台. 3.受限于Windows平台性能,无法解决高性能场景. 二.优势 1.支持跨平台.开源.系统建设成本低. 2.效率和性能较好. 三.缺点 1. ...
- POJ1611(The Suspects)--简单并查集
题目在这里 关于SARS病毒传染的问题.在同一个组的学生是接触很近的,后面也会有新的同学的加入.其中有一位同学感染SARS,那么该组的所有同学得了SARS.要计算出有多少位学生感染SARS了.编号为0 ...
- CSS_引入方式
一 CSS的引入方式 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,用来控制网页数据的表现,可以使网页的表现与数据内容分离 1.行内式 ...
- day24-python之面向对象
1.面向对象 # name='元昊' # # gender='母' # # type='藏獒' #狗的特征 dog1={ 'name':'元昊', 'gender':'母', 'type':'藏獒' ...
- SVM-支持向量机总结
一.SVM简介 (一)Support Vector Machine 支持向量机(SVM:Support Vector Machine)是机器学习中常见的一种分类算法. 线性分类器,也可以叫做感知机,其 ...
- linux uniq命令用法
uniq命令: 对指定的ASCII文件或标准输入进行唯一性检查,以判断文本文件中重复出现的行,常用于分析日志:查看tcp各个状态连接数,ip或域名连接数排名等等场景,一般与 sort 命令结合使用. ...
- jar找不到问题解决
1.File->Settings->搜maven->看Local repository的路径配置是否正确,再看User settings file路径配置是否正确,再看xml内容配置 ...
- Image Processing and Computer Vision_Review:A Performance Evaluation of Local Descriptors——2005.08
翻译 本地描述符的性能评估——http://tongtianta.site/paper/56756 摘要 - 在本文中,我们比较了为局部感兴趣区域计算的描述符的性能,例如,由Harris-Affine ...
- 交付Dubbo微服务到kubernetes集群
1.基础架构 1.1.架构图 Zookeeper是Dubbo微服务集群的注册中心 它的高可用机制和k8s的etcd集群一致 java编写,需要jdk环境 1.2.节点规划 主机名 角色 ip hdss ...