前言

今天跟小伙伴们分享一个实战内容,使用Spring Boot+Shiro实现一个简单的Http认证。

场景是这样的,我们平时的工作中可能会对外提供一些接口,如果这些接口不做一些安全认证,什么人都可以访问,安全性就太低了,所以我们的目的就是增加一个接口的认证机制,防止别人通过接口攻击服务器。

至于Shiro是什么,Http的Basic认证是什么,王子就简单介绍一下,详细内容请自行了解。

Shiro是一个Java的安全框架,可以简单实现登录、鉴权等等的功能。

Basic认证是一种较为简单的HTTP认证方式,客户端通过明文(Base64编码格式)传输用户名和密码到服务端进行认证,通常需要配合HTTPS来保证信息传输的安全。

实践部分

首先说明一下测试环境。

王子已经有了一套集成好Shiro的Spring Boot框架,这套框架详细代码就不做展示了,我们只来看一下测试用例。

要测试的接口代码如下:

/**
* @author liumeng
*/
@RestController
@RequestMapping("/test")
@CrossOrigin
public class TestAppController extends BaseController {
/**
* 数据汇总
*/
@GetMapping("/list")
public AjaxResult test()
{
return success("测试接口!");
}
}

使用Shiro,一定会有Shiro的拦截器配置,这部分代码如下:

  /**
* Shiro过滤器配置
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,这个属性是必须的
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 身份认证失败,则跳转到登录页面的配置
shiroFilterFactoryBean.setLoginUrl(loginUrl);
// 权限认证失败,则跳转到指定页面
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
// Shiro连接约束配置,即过滤链的定义
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 对静态资源设置匿名访问
filterChainDefinitionMap.put("/favicon.ico**", "anon");
filterChainDefinitionMap.put("/lr.png**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/ajax/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/lr/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
// 退出 logout地址,shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
// 不需要拦截的访问
filterChainDefinitionMap.put("/login", "anon,captchaValidate");
filterChainDefinitionMap.put("/ssoLogin", "anon");
     // 开启Http的Basic认证
filterChainDefinitionMap.put("/test/**", "authcBasic");
// 注册相关
filterChainDefinitionMap.put("/register", "anon,captchaValidate"); Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("onlineSession", onlineSessionFilter());
filters.put("syncOnlineSession", syncOnlineSessionFilter());
filters.put("captchaValidate", captchaValidateFilter());
filters.put("kickout", kickoutSessionFilter());
// 注销成功,则跳转到指定页面
filters.put("logout", logoutFilter());
shiroFilterFactoryBean.setFilters(filters); // 所有请求需要认证authcBasic
filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean;
}

这里我们要关注的是代码中的

filterChainDefinitionMap.put("/test/**", "authcBasic"); 

这部分代码,它指定了我们的测试接口启动了Http的Basic认证,这就是我们的第一步。

做到这里我们可以尝试的去用浏览器访问一下接口,会发现如下情况:

这就代表Basic认证已经成功开启了,这个时候我们输入系统的用户名和密码,你以为它就能成功访问了吗?

答案是否定的,我们只是开启了认证,但并没有实现认证的逻辑。

王子通过阅读部分Shiro源码,发现每次发送请求后,都会调用ModularRealmAuthenticator这个类的doAuthenticate方法,源码如下:

    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms();
if (realms.size() == 1) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}

可以看出,这个方法主要就是对Realm进行了管理,因为我们的系统本身已经有两个Ream了,针对的是不同情况的权限验证,所以为了使用起来不冲突,我们可以继承这个类来实现我们自己的逻辑,在配置类中增加如下内容即可:

    @Bean
public ModularRealmAuthenticator modularRealmAuthenticator(){
//用自己重新的覆盖
UserModularRealmAuthericator modularRealmAuthericator = new UserModularRealmAuthericator();
modularRealmAuthericator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthericator;
}

然后在我们自己的UserModularRealmAuthericator类中重写doAuthenticate方法就可以了,这里面的具体实现逻辑就要看你们自己的使用场景了。

我们可以自己新创建一个Realm来单独校验Basic认证的情况,或者共用之前的Realm,这部分就自由发挥了。

大概内容如下:

public class UserModularRealmAuthericator extends ModularRealmAuthenticator {
private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthericator.class); @Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
//强制转换返回的token
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;//所有Realm
Collection<Realm> realms = getRealms();
//最终选择的Realm
Collection<Realm> typeRealms = new ArrayList<>();
for(Realm realm:realms){
if(...){ //这部分是自己的逻辑判断,过滤出想要使用的Realm
typeRealms.add(realm);
}
}
//判断是单Realm 还是多Realm
if(typeRealms.size()==1){
return doSingleRealmAuthentication(typeRealms.iterator().next(),usernamePasswordToken);
}else{
return doMultiRealmAuthentication(typeRealms,usernamePasswordToken);
}
}
}

Realm的具体实现代码这里就不做演示了,无非就是判断用户名密码是否能通过校验的逻辑。如果不清楚,可以自行了解Realm的实现方式。

Realm校验实现后,Basic认证就已经实现了。

测试部分

接下来我们再次使用浏览器对接口进行测试,输入用户名和密码,就会发现接口成功响应了。

我们来抓取一下请求情况

可以发现,Request Header中有了Basic认证的信息Authorization: Basic dGVzdDoxMjM0NTY=

这部分内容是这样的,Basic为一个固定的写法,dGVzdDoxMjM0NTY=这部分内容是userName:Password组合后的Base64编码,所以我们只要给第三方提供这个编码,他们就可以通过编码访问我们的接口了。

使用PostMan测试一下

可以发现接口是可以成功访问的。

总结

到这里本篇文章就结束了,王子向大家仔细的介绍了如何使用Shiro实现一个Http请求的Basic认证,是不是很简单呢。

给大家留下一个思考题,你认为这种Basic认证能够保证接口的安全吗?

欢迎大家留言讨论。

往期文章推荐:

JVM专栏

消息中间件专栏

并发编程专栏

Shiro实现Basic认证的更多相关文章

  1. SpringBoot&Shiro实现用户认证

    SpringBoot&Shiro实现用户认证 实现思路 思路:实现认证功能主要可以归纳为3点 1.定义一个ShiroConfig配置类,配置 SecurityManager Bean , Se ...

  2. [ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证

    很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前请求者是一个经过授权的用户.授权的本质就是让用户在他许可的权限范围内做他能够做的事情,授权的前提是请求者 ...

  3. iOS进行Basic认证与NTLM认证

    一.iOS进行Basic认证 只需要在NSMutableURLRequest的Header中添加认证所需的Username和password. NSMutableURLRequest *webReq ...

  4. Nginx 配置 Basic 认证

    /* * 环境:LNMP(CentOS 6.6 + Nginx 1.8.0) */ 在 Nginx 下配置 Basic 认证需要依靠 Nginx 的 http_auth_basic_module 模块 ...

  5. Apache 配置 Basic 认证

    /* * 环境:WAMP( Windows7 + WampServer2.2(Apache 2.2.21)) */ 配置过程: ① 生成用户文件,文件路径可以使用绝对路径,也可以使用相对路径 进入 a ...

  6. Basic认证

    Basic 概述 Basic 认证是HTTP 中非常简单的认证方式,因为简单,所以不是很安全,不过仍然非常常用. 当一个客户端向一个需要认证的HTTP服务器进行数据请求时,如果之前没有认证过,HTTP ...

  7. Http Basic认证

    Http Basic认证就是访问的时候把用户名和密码用base64加密放在request的header的authorization中 服务端直接获取authorization,解析,跟用户名匹配即可. ...

  8. Burpsuite之Http Basic认证爆破

    有的时候经常遇到401.今天正好朋友问怎么爆破,也顺便记录一下 怕忘记了 referer:http://www.2cto.com/Article/201303/194449.html 看到Burpsu ...

  9. HTTP使用BASIC认证的原理及实现方法

    一.   BASIC认证概述 在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务器进行数据请求时,如果客户 ...

随机推荐

  1. swlib

    Swoole人性化组件库 ~ Swoole Humanization Component Library

  2. 记php多张图片 合并生成竖列 纵向长图(可用于商品详情图合并下载)

    <?php namespace app\mapi\common\image; /** * 拼接多幅图片成为一张图片 * * 参数说明:原图片为文件路径数组,目的图片如果留空,则不保存结果 * * ...

  3. python 中 try...finally... 的优雅实现

    1. 关于 try.. finally.. 假如上帝用 python 为每一个来到世界的生物编写程序,那么除去中间过程的种种复杂实现,最不可避免的就是要保证每个实例最后都要挂掉.代码可简写如下: tr ...

  4. jsp自定义错误页

    1.在可能出错页面的Page标签上errorPage="/Error.jsp"指定错误页 2.Error,jsp的page标签中isErrorPage="true&quo ...

  5. NameServer 与zk

    1.nameServer 之间互不通信,故不存在强一致性,即不同的producer看到的视图可能时不一样的,(如何保证最终一致的?) 2.nameServer维护的boker信息 发生变化时,不会像z ...

  6. EntityFramework Core健康检查

    前言 .NET Core提供对应方法可进行健康检查,那么在EF Core中是否也提供了相应的方式呢?EF Core 2.2+(包含2.2)版本提供了针对上下文的健康检查,接下来我们直接利用.NET 5 ...

  7. 重做系统后 恢复oracle 实例

    第一次进行操作,按照网上方法,试了一遍,很多细节搞不清楚,但是还是要记录一下. 备份 old数据库 所用到文件有: XXXXXX\product\11.2.0\dbhome_1\database\pw ...

  8. Kafka速度为什么那么快

    记录一下 Kafka速度为什么那么快 Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率. 即使是普通 ...

  9. keil/MDK代码配色

    个人配色方案,仅供参考.

  10. 简单dp水题

    #include <bits/stdc++.h> using namespace std; #define limit (100 + 5)//防止溢出 #define INF 0x3f3f ...