IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 接入Shiro(1)——极简登录认证 -> 正文阅读

[Java知识库]接入Shiro(1)——极简登录认证

Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。之前的开发都是在低代码上直接使用Shiro,一般也不需要修改。

在SpringBoot的基础上,只接入Shiro的登录认证还是头一次。看各方文档和代码,不是一大坨概念讲的云里雾里,就是一大坨代码看的眼花缭乱。认证和授权拌在一起,咽又咽不下去,咽了一点也消化不了。

花了一天时间终于从JeecgBoot的代码中拆出了登录认证,又花了一天时间做了个极简版本,现在分享给大家。

一、背景

后端基于SpringBoot,前端基于vue使用antdv的组件,即前后端分离。

二、准备

1、添加Shiro依赖

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.9.0</version>
</dependency>

2、前端请求

为了简化前端,直接使用Swagger2的API接口文档做前端测试。

Swagger2的接入,请参考之前的博文,接入knife4(3.0.3)

3、后端接口

后端添加俩测试用的接口,一个接口不需要认证,另一个接口需要认证,代码如下:

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api(tags = "测试Shiro")
@RestController
@RequestMapping("/demo")
public class DemoRC {

    /**
     * 这个接口不需要认证
     */
    @ApiOperation("Get1")
    @GetMapping("/get1")
    String get() {
        return "get1";
    }

    /**
     * 这个接口需要认证
     */
    @ApiOperation("Get2")
    @GetMapping("/get2")
    String get2() {
        return "get2";
    }
}

三、接入Shiro

1、添加Shiro配置

配置是过滤器的第一步,需要告诉框架,Shiro要处理什么样的网络请求,代码如下:

import com.example.demo.config.shiro.filters.CustomFilter;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shirFilter() {
        // Swagger相关地址放入白名单
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/doc.html", "anon");
        filterChainDefinitionMap.put("/swagger**/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/v3/**", "anon");

        // 测试接口放入白名单(不需要认证)
        filterChainDefinitionMap.put("/demo/get1", "anon");

        // 白名单之外的,都得通过该过滤器
        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
        filterMap.put("custom", new CustomFilter());
        filterChainDefinitionMap.put("/**", "custom");

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        // 设置过滤器
        shiroFilterFactoryBean.setFilters(filterMap);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建默认安全管理对象
     */
    private DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置自己的认证员(只有自己知道token与用户名的关系)
        securityManager.setRealm(new CustomRealm());
        return securityManager;
    }
}

其中CustomFilter和CustomRealm是自定义类,下面来定义这俩类。

2、过滤器CustomFilter

import com.example.demo.config.shiro.CustomToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomFilter extends BasicHttpAuthenticationFilter {

    /**
     * 是否允许通过,只做最基本的判断
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            return executeLogin(request, response);
        } catch (Exception e) {
            return false;
        }
    }

    /**`
     * 执行登录
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws IOException {
        // 获取Token
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        String token = httpServletRequest.getHeader("ACCESS-TOKEN");
        // Token验证
        try {
            CustomToken customToken = new CustomToken(token);
            getSubject(request, response).login(customToken);
        } catch (AuthenticationException e) {
            HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
            httpServletResponse.sendError(401, e.getLocalizedMessage());
            return false;
        }
        return true;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        {
            httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
            // 是否允许发送Cookie,默认Cookie不包括在CORS请求之中。设为true时,表示服务器允许Cookie包含在请求中。
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
        }
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
}

如果是前后端不分离的,不需要实现方法preHandle。

这里要求前端发送的token保存在Header的“ACCESS-TOKEN”中,根据实际情况自己修改。

3、CustomToken

自定义一个Token类,以便做进一步的认证,代码如下:

import org.apache.shiro.authc.AuthenticationToken;

public class CustomToken implements AuthenticationToken {
    private final String token;

    public CustomToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return this.token;
    }

    @Override
    public Object getCredentials() {
        return this.token;
    }

    public String getToken() {
        return this.token;
    }
}

4、进一步认证CustomRealm

import org.apache.shiro.authc.*;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.springframework.stereotype.Component;

@Component
public class CustomRealm extends AuthenticatingRealm {

    /**
     * 告诉框架,CustomToken类型的Token必须要通过当前类的认证
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof CustomToken;
    }

    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = ((CustomToken)auth).getToken();
        if (token == null || token.length() < 1) {
            throw new AuthenticationException("未登录,请进行登录!");
        }
        // 假设token长度等于8才是正确的
        if (token.length() != 8) {
            throw new AuthenticationException("token错误!");
        }
        // 从token中得到用户名
        String username = token.substring(0, 4);
        // 根据用户名得到用户信息
        Object userInfo = username;
        return new SimpleAuthenticationInfo(userInfo, token, getName());
    }
}

四、测试

1、开启动态参数请求,以便修改Header

打开API接口文档,点击菜单“文档管理” -> “个性化设置”,勾选“开启动态请求参数”,然后刷新页面。如下图:

?2、测试

(1)不带Header

点击菜单“测试Shiro”下的“Get1”和“Get2”,分别请求,可以发现Get1能正常请求到数据,Get2请求的状态码是401,是token不存在时抛出的异常。如下图:

(2)token长度错误

Get2,添加请求头。请求头名称“ACCESS-TOKEN”,请求头内容“123456”。依然返回401,token长度不等于8时抛出的异常。如下图:

?(3)认证通过

请求头中输入“12345678”,正确得到了返回结果。如下图:

五、结束

终于,终于弄通了Shiro认证的基本流程,下一步处理登录逻辑生成token,然后进行token认证。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-03 23:53:36  更:2022-06-03 23:53:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:35:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码