栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

shiro和jwt(shiro jwt redis)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

shiro和jwt(shiro jwt redis)

shiro是什么

用于进行加密,认证授权的java安全框架
核心对象:
Subject:用户对象,封装信息
SecurityManager:管理所有用户
Realm:连接数据,进行用户认证和授权

Jwt是什么

JWT全称JSON Web Token,是javaweb官方提供的一种封装用户数据,保证权限访问的字符串。
详情见:https://blog.csdn.net/m0_51433562/article/details/119609637?spm=1001.2014.3001.5502

原理讲解

知道了Shiro和Jwt是什么玩意儿之后,那么接下来我们要探究如何使用Shiro和Jwt完成用户认证和授权的呢?

认证(Authentication)和授权(Authorization)

首先我们先弄懂认证和授权到底有什么区别,平常我们都是将认证授权两个词语连在一起进行讨论,实际上认证和授权是两个完全不同的概念。

举个很简单的例子,我们去网吧上网的时候,网管首先会让你出示你的身份证件,核实你的身份。确定你是成年人之后,网管会激活你的号码或者会员,这样你就可以在网吧内入座了。

在上述的例子中,网管核实你是否为成年人就是一个认证的过程。认证通过之后,网管激活号码,你获得在网吧内就做的权利,这就是一个授权的过程。相信通过这个案例,大家对于认证和授权应该有了理解。

Shiro的认证和授权

那么在shiro安全框架中又是怎样实现认证和授权的呢?
在javaWeb开发中,我们会遇到这样的场景,某些页面或者接口是只有特定的用户可以进行访问和调用的,比如VIP视频只允许VIP用户观看,后台管理接口只允许管理员登录。此时shiro就能很好的帮助我们完成这个需求。


分析上图:

    客户端发送携带JwtToken的请求到后端,会根据Sercurity Manager配置的拦截器进行判断,进入不同的Filter过滤器中(以JwtFilter为例)。Filter过滤器会对请求进行拦截,获取请求携带的Token。在过滤器中创建subject对象,这相当与一个用户对象,利用subject对象并携带从客户端获取的token,调用Sercurity Manager,将subject对象统一交给Sercurity Manager去处理。(注:原生shiro中subject对象会对传递来的username和password进行封装形成token,因为我们采用的是jwt,传来的就是token,所以不必封装)。subject对象进入Sercurity Manager后,会将携带的Token交给JwtRealm的认证授权程序进行处理,认证通过后,放可执行controller中的请求,否则会抛出异常。
代码编写

了解了shiro和JWT是如何共同实现认证和授权之后,就可以很轻松的完成代码的编写。先介绍我们要用到的类

ShiroCofig:Shiro的配置类,用于配置subject,securityManager和realm。JwtRealm:自定义的Realm对象,用于连接数据,进行认证和授权。JwtToken:自定义的token类,用以代替shiro原生的UsernamePasswordTokenJwtDefaultSubjectFactory(可选):自定义的subjectFactory,继承于DefaultSubjectFactory,用于生产subject对象。JwtFilter:需要进行jwt认证的API接口经过的过滤器。CommonFilter:不需要进行jwt认证的API接口经过的过滤器。JwtUtils:JWT的工具类,用于decode,encode和识别token(具体代码在前面提到的博客中有) ShiroConfig

ShiroConfig用于进行Shiro的相关配置,主要包括ShiroFilterFactoryBean、DefaultWebSecurityManager和Realm的配置。

ShiroFilterFactoryBean:用来生产subject,配置过滤器和拦截路由。DefaultWebSecurityManager:获得SecurityManager,对subject进行统一管理。还可以进行Session Dao和Session Manager等的配置(相关配置,本文章省略)Realm:放回自定义的Realm对象

@Configuration
public class ShiroConfig {
    //三大核心对象:Subject、SecurityManager、Realm

    //告诉shiro不创建内置的session
    @Bean
    public SubjectFactory subjectFactory(){
        return new JwtDefaultSubjectFactory();
    }

    //3、ShiroFilterFactoryBean->Subject subject是用户主题,进入到securitymanager中
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //关联securityManager
        bean.setSecurityManager(getDefaultWebSecurityManager());
        //添加内置过滤器
        Map filterMap = new HashMap<>();
        filterMap.put("anon",new CommonFilter());
        filterMap.put("jwt",new JwtFilter());
        bean.setFilters(filterMap);
        //添加拦截器,对路由进行限制
        Map filterRuleMap = new linkedHashMap<>();
        
        filterRuleMap.put("/treasure/user/login","anon");
        filterRuleMap.put("/treasure/user/getUserInfoByEmail
    @Override
    public boolean supports(AuthenticationToken token) {
        //这个token就是从过滤器中传入的jwtToken
        return token instanceof JwtToken;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String jwt = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("user");
        return authorizationInfo;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String jwt = (String) authenticationToken.getPrincipal();
        if(!jwtUtils.isVerify(jwt)){
            throw new IncorrectCredentialsException("Authorization token is invalid");
        }
        return new SimpleAuthenticationInfo(jwt,jwt,"JwtRealm");
    }
}

JwtToken

JwtToken是定义的一个Token类,继承了AuthenticationToken类,实现getPrincipal和getCredentials方法,(这两个方法本来是用于获取token中的信息,和识别token的,但JwtUtils已经为我们提供了这样的方法,所以这两个方法对于jwtToken没有意义)。用于将客户端传来的token进行封装,便于Realm识别token类型,进行认证和授权。

public class JwtToken implements AuthenticationToken {

    private String jwt;

    public JwtToken(String jwt) {
        this.jwt = jwt;
    }

    //返回原来的字符串,解析交给JwtUtils实现
    @Override
    public Object getPrincipal() {
        return jwt;
    }

    //返回原来的字符串,解析交给JwtUtils实现
    @Override
    public Object getCredentials() {
        return jwt;
    }
}
JwtDefaultSubjectFactory

继承于DefaultSubjectFactory类,重写了生产工厂,关闭Shiro的session存储

public class JwtDefaultSubjectFactory extends DefaultSubjectFactory {

    @Override
    public Subject createSubject(SubjectContext context) {
        //不创建shiro内部的session
        context.setSessionCreationEnabled(false);
        return super.createSubject(context);
    }
}
JwtFilter

JwtFilter定义为需要进行jwt认证的API结果经过的过滤器,在这个过滤器中,取出API请求中包含的JWT字符串,封装为jwtToken对象,同时创建一个subject,调用login方法进行认证和授权。JwtFilter继承于AccessControlFilter类,实现isAccessAllowed和onAccessDenied方法。

//需要认证的url经过该过滤器
public class JwtFilter extends AccessControlFilter {

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin"));
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Content-Type","application/json;charset=UTF-8");
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());
            httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));
            return true;
        }
        return super.preHandle(request, response);
    }

    
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        String jwt = ((HttpServletRequest) servletRequest).getHeader("Authorization");
        if(jwt != null){
            JwtToken jwtToken = new JwtToken(jwt);
            //这里getSubject方法实际上就是获得一个subject
            //与原生shiro不同的地方在于没有对username和password进行封装
            //直接使用jwt进行认真,login方法实际上就是交给Realm进行认证
            try{
                getSubject(servletRequest,servletResponse).login(jwtToken);
            }catch (Exception e){
                e.printStackTrace();
                return false;
            }
            return true;
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        //直接设置401 未认证
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        return false;
    }
}
CommonFilter

CommonFilter是不需要进行认证的API接口经过的过滤器,只需要进行跨域处理

// 不需要进行认证的url经过该过滤器
public class CommonFilter extends AnonymousFilter {

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin"));
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Content-Type","application/json;charset=UTF-8");
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());
            httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));
            return true;
        }
        return super.preHandle(request, response);
    }
}

写了这么多类,大家可能有点懵,实际上仔细看看代码里面的注释还是很容易理解的。为了更清晰的描述之间的关系,见下图。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/775776.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号