spring---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

读者福利:

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容