阿里Java程序员告诉你——Spring Boot:Shiro拦截器 看完这一篇你就懂了

拦截器

1.结构架构图

2.Shiro拦截器

2.1.ShiroConfig

代码:

package com.auth;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
    @Autowired
    private LoginRealm  loginRealm;
    /**
     * 配置安全管理器:哪种类型的管理器
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        /*
         * 关闭shiro自带的session,详情见文档
         * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
         */
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        // 设置自定义 realm.
        securityManager.setRealm(loginRealm);
        return securityManager;
    }
    /**
     * 配置拦截器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean factory(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);

        Map filterMap = new HashMap();
        factoryBean.setFilters(filterMap);

        // 设置无权限时跳转的 url;
        Map<String, String> filterRuleMap = new HashMap();
        //访问/login和/unauthorized 不需要经过过滤器

        //设置我们自定义的JWT过滤器
        filterMap.put("jwt", new JWTFilter());

        //不登录就能访问的写这里
        //filterRuleMap.put("/api2/**", "anon");
        filterRuleMap.put("/api/**","jwt,authc");//此网页需要过滤判断以后才能通过

        //必须要登录才能访问写这里
        filterRuleMap.put("/**", "anon");

        // 访问 /unauthorized/** 不通过JWTFilter
        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

}

2.1.1.该类是一个config类(配置类)
2.1.2.设置我们自定义的JWT过滤器,并命名为jwt
filterMap.put("jwt", new JWTFilter());

2.1.3.必须要登录才能访问(要验证令牌,使用"jwt,authc",一般用于删除、修改、查询等界面)
filterRuleMap.put("/api/**","jwt,authc");

2.1.4.不登录就能访问(直接放行,不需要去做验证,一般用于登录,首页等界面)
filterRuleMap.put("/**", "anon");

2.2.JWTFilter

代码:

package com.auth;

import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
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;

public class JWTFilter extends BasicHttpAuthenticationFilter {

    /**
     * 第二步
     * 是否能进下一步处理
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return true可以访问action了,false则执行onAccessDenied
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {

            //System.out.println("isAccessaLLOWED");
            //得到客户端传过来的令牌
            String token = ((HttpServletRequest) request).getHeader("token");
            //System.out.println("客户端令牌"+token);
            //封装
            JWTToken jwtToken = new JWTToken(token);
            //登录:实际是调用LoginRealm里面doGetAuthenticationInfo
            getSubject(request, response).login(jwtToken);
            //判断是否有权限
            String url = ((HttpServletRequest) request).getRequestURI();
            getSubject(request, response).checkPermission(url);

            return true;

        } catch (Exception e) {
            return false;
        }
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        System.out.println("onAccessDenied");
        HttpServletResponse r = (HttpServletResponse) response;
        r.setStatus(401);
        return false;
    }

    /**
     * 用于跨域   第一步
     *
     * @param request
     * @param response
     * @return true可以进下一步isAccessAllowed,如果false则被拦截不能通过
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        //System.out.println("这是preHandle");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.addHeader("Access-Control-Allow-Headers", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
        httpServletResponse.addHeader("Access-Control-Expose-Headers", "token");

        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }

        //更新令牌且发送给客户端
        String token = ((HttpServletRequest) request).getHeader("token");
        //System.out.println("更新令牌"+token);
        String newToken = JwtUtil.updateToken(token);
        if (newToken.length() > 0) {
            httpServletResponse.addHeader("token", newToken);
            //System.out.println("更新以后的令牌判断"+token);
        }

        return super.preHandle(request, response);
    }
}

2.2.1.用于跨域
2.2.2更新令牌且发送给客户端放在header里的原因是,最好在body里放数据。当newToken.length()等于0时,说明令牌失效或者是错误的。
String token = ((HttpServletRequest) request).getHeader("token");
String newToken = JwtUtil.updateToken(token);
if (newToken.length() > 0) {
    httpServletResponse.addHeader("token", newToken);
}
return super.preHandle(request, response);

2.3.JWTToken

代码:

package com.auth;

import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken {
    private String token;

    public JWTToken(){}

    /**
     * Instantiates a new Jwt token.
     *
     * @param token the token
     */
    public JWTToken(String token) {
        this.token = token;
    }

    /**
     * 以前的用户名
     * @return
     */
    @Override
    public Object getPrincipal() {
        return token;
    }

    /**
     * 以前的密码
     * @return
     */
    @Override
    public Object getCredentials() {
        return token;
    }
}

2.3.1.相当于pojo,里面有用户名、密码
2.4.LoginRealm

代码:

ackage com.auth;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

@Component
public class LoginRealm extends AuthorizingRealm {

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }
    /**
     * 管授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String tokenString = principals.toString();
        String username = JwtUtil.getUsername(tokenString);

        //查数据库,用户的权限,略,通过用户名从数据库查
        authorizationInfo.addStringPermission("/api/show");
        return authorizationInfo;
    }

    /**
     * 管登录
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //JWTFilter传过来的令牌
        String tokenString =(String) token.getPrincipal();
        //System.out.println("JWTFilter传过来的令牌"+tokenString);
        //取用户名,如果为null则无效
        String username = JwtUtil.getUsername(tokenString);
        if (username==null)
        {
            throw new RuntimeException("令牌无效");
        }
        return new SimpleAuthenticationInfo(tokenString, tokenString, getName());

    }
}

2.4.1.主要是两个功能,一个是管理登录,还有一个是管理权限
2.4.2.JWTFilter传过来的令牌
String tokenString =(String) token.getPrincipal();

2.4.3.取用户名,如果为null则无效返回的new SimpleAuthenticationInfo(tokenString, tokenString, getName()),其实是一个布尔类型即true或者false
String username = JwtUtil.getUsername(tokenString);
if (username==null)
{
    throw new RuntimeException("令牌无效");
}
return new SimpleAuthenticationInfo(tokenString, tokenString, getName());

2.4.4.查数据库,用户的权限,略
authorizationInfo.addStringPermission("/api/searchDep");authorizationInfo.addStringPermission("/api/removeDep");

2.5.JwtUtil

代码:

package com.auth;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

@Component
public class JwtUtil {
    //密码,绝对保密,写在配置文件中
    public static String sercetKey = "mingtianhenganghao";
    public final static long keeptime = 180000000;

   /* @Value("${token.sercetKey}")
    public  static String sercetKey;
    @Value("${token.keeptime:30000}")
    public static long keeptime;*/

    public static void main(String[] args) {

        //根据用户名产生令牌
        // String token = JwtUtil.generToken("admin", null,null);
        //System.out.println(token);

        //根据令牌取用户名
        String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJhZG1pbiIsImlhdCI6MTU5OTkxMzA5MSwiZXhwIjoxNTk5OTMxMDkxfQ.9_P_o9hwbsdlo2517jfSUoB3flUvCvOg1m96MCwHkks";
        String username = JwtUtil.getUsername(token);
        System.out.println(username);
    }

    /**
     * 产生令牌
     * @param id  用户名
     * @param issuer  签发者
     * @param subject 主体(内容)
     * @return
     */
    public static String generToken(String id, String issuer, String subject) {
        long ttlMillis = keeptime;
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        JwtBuilder builder = Jwts.builder().setId(id)
                .setIssuedAt(now);
        if (subject != null) {
            builder.setSubject(subject);
        }
        if (issuer != null) {
            builder.setIssuer(issuer);
        }
        builder.signWith(signatureAlgorithm, signingKey);

        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * 更新令牌
     * @param token
     * @return
     */
    public static String updateToken(String token) {
        try {
            Claims claims = verifyToken(token);
            String id = claims.getId();
            String subject = claims.getSubject();
            String issuer = claims.getIssuer();
            Date date = claims.getExpiration();
            return generToken(id, issuer, subject);
        } catch (Exception ex) {
            //  ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取用户名
     * @param token
     * @return
     */
    public static String getUsername(String token) {

        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
                    .parseClaimsJws(token).getBody();
            return claims.getId();
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return null;
    }

    public static Claims verifyToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
                .parseClaimsJws(token).getBody();
        return claims;
    }
}

2.5.1.三个功能:将用户信息变为令牌,将令牌还原,更新令牌(设置令牌失效时间)
2.5.2.产生令牌
public static String generToken(String id, String issuer, String subject) {
    long ttlMillis = keeptime;
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    long nowMillis = System.currentTimeMillis();
    Date now = new Date(nowMillis);
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
    Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

    JwtBuilder builder = Jwts.builder().setId(id)
            .setIssuedAt(now);
    if (subject != null) {
        builder.setSubject(subject);
    }
    if (issuer != null) {
        builder.setIssuer(issuer);
    }
    builder.signWith(signatureAlgorithm, signingKey);

    if (ttlMillis >= 0) {
        long expMillis = nowMillis + ttlMillis;
        Date exp = new Date(expMillis);
        builder.setExpiration(exp);
    }
    return builder.compact();
}

2.5.3.还原令牌信息(获取用户名)
public static String getUsername(String token) {

    try {
        Claims claims = Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
                .parseClaimsJws(token).getBody();
        return claims.getId();
    } catch (Exception e) {
        //e.printStackTrace();
    }
    return null;
}

2.5.4.更新令牌(设置令牌实效时间)
public static String updateToken(String token) {
    try {
        Claims claims = verifyToken(token);
        String id = claims.getId();
        String subject = claims.getSubject();
        String issuer = claims.getIssuer();
        Date date = claims.getExpiration();
        return generToken(id, issuer, subject);
    } catch (Exception ex) {
        //  ex.printStackTrace();
    }
    return "";
}

(设置实效时间)

/* @Value("${token.sercetKey}") public  static String sercetKey; @Value("${token.keeptime:30000}") public static long

读者福利:

由于篇幅限制,不能完整的把全部内容分享出来,如果你觉得此文档对你有帮助,那就关注+点赞哦

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值