最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

shiro+jwt登陸驗證實現(xiàn)教程

 更新時間:2026年03月13日 10:08:34   作者:我是菜鳥吶  
本文介紹了如何使用Shiro和JWT實現(xiàn)登錄認(rèn)證,并通過自定義Realm和過濾器來完成身份驗證和授權(quán),通過測試,展示了如何攜帶JWT令牌進(jìn)行接口訪問,并驗證了其有效期

學(xué)習(xí)目標(biāo)

例如:

  • 一小時實現(xiàn)shiro+jwt實現(xiàn)登錄認(rèn)證

學(xué)習(xí)內(nèi)容

例如:

  1. 了解shiro框架
  2. 使用jwt生成身份令牌

項目目錄:

直接上代碼

這個過濾器是我們的重點,這里我們繼承的是Shiro內(nèi)置的BasicHttpAuthenticationFilter ,一個可以內(nèi)置了可以自動登錄方法的的過濾器,有些同學(xué)繼承AuthenticatingFilter也是可以的,根據(jù)個人情況。

@Component
@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {


    /**
     * 認(rèn)證之前執(zhí)行該方法
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        Subject subject = SecurityUtils.getSubject();
        return  null != subject && subject.isAuthenticated();
    }

    /**
     * 認(rèn)證未通過執(zhí)行該方法
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response){
        //完成token登入
        //1.檢查請求頭中是否含有token
        HttpServletRequest httpServletRequest= (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Authorization");
        //2. 如果客戶端沒有攜帶token,攔下請求
        if(null==token||"".equals(token)){
            responseTokenError(response, RCode.INVALID_TOKEN);
            return false;
        }
        //3. 如果有,對進(jìn)行進(jìn)行token驗證
        JWTToken jwtToken = new JWTToken(token);
        try {
            SecurityUtils.getSubject().login(jwtToken);
        } catch (AuthenticationException e) {
            log.error(e.getMessage());
            responseTokenError(response,RCode.AUTH_ERROR);
            return false;
        }

        return true;
    }

    /**
     * 對跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) 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"));
        // 跨域時會首先發(fā)送一個option請求,這里我們給option請求直接返回正常狀態(tài)
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
    /**
     * 無需轉(zhuǎn)發(fā),直接返回Response信息 Token認(rèn)證錯誤
     */
    private void responseTokenError(ServletResponse response,RCode rCode) {
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setStatus(HttpStatus.OK.value());
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        try (PrintWriter out = httpServletResponse.getWriter()) {
            String data = new Gson().toJson(R.error(rCode));
            out.append(data);
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }
}

整合shiro的配置,shiro可以幫我們做很多事情,比如創(chuàng)建realm、配置shiro過濾器工廠等。

@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public DefaultWebSecurityManager getManager(UserRealm realm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // 使用自己的realm
        manager.setRealm(realm);

        /*
         * 關(guān)閉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);
        manager.setSubjectDAO(subjectDAO);

        return manager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        // 添加自己的過濾器并且取名為jwt
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("jwt", new JWTFilter());
        factoryBean.setFilters(filterMap);

        factoryBean.setSecurityManager(securityManager);
        /*
         * 自定義url規(guī)則
         * http://shiro.apache.org/web.html#urls-
         */
        Map<String, String> filterRuleMap = new HashMap<>();
        // 所有請求通過我們自己的JWT Filter
        filterRuleMap.put("/**", "jwt");
        // 訪問401和404頁面不通過我們的Filter
        filterRuleMap.put("/admin/sys/user/login", "anon");
        filterRuleMap.put("/user/imgCode", "anon");
        //開放API文檔接口
        filterRuleMap.put("/swagger-ui.html", "anon");
        filterRuleMap.put("/webjars/**","anon");
        filterRuleMap.put("/swagger-resources/**","anon");
        filterRuleMap.put("/v2/**","anon");
        //sql監(jiān)控
        filterRuleMap.put("/druid/**","anon");
        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }
    /**
     * 下面的代碼是添加注解支持
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 強(qiáng)制使用cglib,防止重復(fù)代理和可能引起代理出錯的問題
        // https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

自定義realm,因為我沒有連接數(shù)據(jù)庫這里的用戶名和密碼都是寫死的,根據(jù)業(yè)務(wù)需要可以連接數(shù)據(jù)庫獲取

@Service
public class UserRealm extends AuthorizingRealm {
    /**
     * 大坑!,必須重寫此方法,不然Shiro會報錯
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }
    /**
     * 只有當(dāng)需要檢測用戶權(quán)限的時候才會調(diào)用此方法,例如checkRole,checkPermission之類的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    /**
     * 默認(rèn)使用此方法進(jìn)行用戶名正確與否驗證,錯誤拋出異常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        if(JWTUtils.isExpire(token)){
            throw new AuthenticationException("token過期,請重新登入!");
        }
        // 解密獲得username,用于和數(shù)據(jù)庫進(jìn)行對比
        String username = JWTUtils.getUsername(token);

        if (username == null) {
            throw new AuthenticationException("token錯誤,請重新登入!");
        }

        String name="admin";
        String pwd="3bcbb857c763d1429a24959cb8de2593";
        if (! JWTUtils.verify(token, name, pwd)) {

            throw new AuthenticationException("密碼錯誤!");
        }

        return new SimpleAuthenticationInfo("activeUser", token, getName());
    }
}
public class JWTToken implements AuthenticationToken {

    // 密鑰
    private String token;

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

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

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

jwt生成token

public class JWTUtils {
    /**
     * 過期時間6小時
     */
    private static final long EXPIRE_TIME = 6 * 60 * 60 * 1000;

    /**
     * 校驗token是否正確
     *
     * @param token  密鑰
     * @param secret 用戶的密碼
     * @return 是否正確
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 獲得token中的信息無需secret解密也能獲得
     *
     * @return token中包含的用戶名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 生成簽名,2min后過期
     *
     * @param username 用戶名
     * @param secret   用戶的密碼
     * @return 加密的token
     */
    public static String sign(String username, String secret) {
        try {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附帶username信息
            return JWT.create()
                    .withClaim("username", username)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 判斷過期
     *
     * @param token
     * @return
     */
    public static boolean isExpire(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return System.currentTimeMillis() > jwt.getExpiresAt().getTime();
    }
}

使用md5進(jìn)行加密

    /**
     * 密碼加密
     * @return
     */
    public static String md5Encryption(String source,String salt){
        String algorithmName = "MD5";//加密算法
        int hashIterations = 1024;//加密次數(shù)
        SimpleHash simpleHash = new SimpleHash(algorithmName,source,salt,hashIterations);
        return simpleHash+"";
    }

重載類:用于結(jié)果返回

@Data
@AllArgsConstructor
@NoArgsConstructor
public class R {
    private Integer code ;
    private String msg;
    private Object data;



    public static R ok(Object data){
        return new R(RCode.SUCCESS.getCode(),
                RCode.SUCCESS.getMsg(),data);
    }
    public static R ok(){
        return R.ok(null);
    }
    public static R error(RCode rCode){
        return new  R(rCode.getCode(),rCode.getMsg(),null);
    }
    public static R error(String message){
        return new  R(RCode.SYS_ERROR.getCode(),message,null);
    }
    public static R error(){
        return R.error(RCode.ERROR);
    }
}

枚舉類

@AllArgsConstructor
@Getter
public enum RCode {
    SUCCESS(0,"操作成功"),
    ERROR(1,"操作失敗"),
    SYS_ERROR(500,"系統(tǒng)錯誤,請與管理員聯(lián)系"),
    SYS_VALID_ERROR(501,"參數(shù)校驗錯誤"),
    USER_EXIST(1001,"用戶已存在"),
    USER_NOT_EXIST(1002,"用戶不存在!"),
    VALID_ERROR(40001,"驗證錯誤!"),
    INVALID_TOKEN(40410,"Token無效,您無權(quán)訪問該接口"),
    AUTH_ERROR(40411,"用戶名或密碼不存在!"),
    NO_AUTH_ERROR(40412,"無權(quán)訪問!"),
    QUERY_EXIST(1002,"此數(shù)據(jù)以重復(fù)")
    ;
    private Integer code;
    private String msg;

}

到這里就可以開始寫controller了,寫了一個登陸的接口和兩個測試的接口

@RestController
@CrossOrigin
@RequestMapping("/admin/sys/user")
public class TestController {

    @GetMapping("/login")
    public R login(String username,String password){
        String MD5Password = MD5Utils.md5Encryption(username, password);
        System.out.println(MD5Password);
        String token= JWTUtils.sign(username, MD5Password);
        System.out.println(token);
        JWTToken jwtToken = new JWTToken(token);
        try {
            SecurityUtils.getSubject().login(jwtToken);
        } catch (AuthenticationException e) {
            return R.error("token 錯誤!");
        }
        Map<String,Object> maps = new HashMap<>();
        maps.put("token",token);
        return R.ok(maps);
    }

    @GetMapping("all")
    public String all(){
        return "all";
    }
	@GetMapping("list")
    public R list(){
        return R.ok("list");
    }
}

測試結(jié)果

如果我不攜帶token是不能訪問任何接口的,除登陸接口外

下面訪問登陸接口,是可以訪問的,并且返回的token令牌,我們可以拿著這個token去訪問其他接口

帶著token就可以正常訪問了,有效期是6個小時

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • win10操作系統(tǒng)下重啟電腦java環(huán)境變量失效

    win10操作系統(tǒng)下重啟電腦java環(huán)境變量失效

    這篇文章主要介紹了win10操作系統(tǒng)下重啟電腦java環(huán)境變量失效,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • IntelliJ IDEA 中配置 Spring MVC 環(huán)境的詳細(xì)步驟及問題解決

    IntelliJ IDEA 中配置 Spring MVC 環(huán)境的詳細(xì)步

    這篇文章主要介紹了IntelliJ IDEA 中配置 Spring MVC 環(huán)境的詳細(xì)步驟及問題解決,本文分步驟結(jié)合實例給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-04-04
  • SpringMVC中的@RequestMapping注解解析

    SpringMVC中的@RequestMapping注解解析

    這篇文章主要介紹了SpringMVC中的@RequestMapping注解解析,SpringMVC使用@RequestMapping注解為控制器指定可以處理哪些?URL?請求,在控制器的類定義及方法定義處都可標(biāo)注@RequestMapping,需要的朋友可以參考下
    2023-12-12
  • SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā)

    SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列re

    這篇文章主要介紹了SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā) 的相關(guān)資料,需要的朋友可以參考下
    2016-02-02
  • Java字符拼接成字符串的注意點詳解

    Java字符拼接成字符串的注意點詳解

    這篇文章主要介紹了Java字符拼接成字符串的注意點詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • Java微信退款開發(fā)

    Java微信退款開發(fā)

    這篇文章主要為大家詳細(xì)介紹了Java微信退款開發(fā)的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Spring-Boot框架初步搭建

    Spring-Boot框架初步搭建

    本篇文章主要介紹了Spring-Boot框架初步搭建,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Spring多線程通過@Scheduled實現(xiàn)定時任務(wù)

    Spring多線程通過@Scheduled實現(xiàn)定時任務(wù)

    這篇文章主要介紹了Spring多線程通過@Scheduled實現(xiàn)定時任務(wù),@Scheduled?定時任務(wù)調(diào)度注解,是spring定時任務(wù)中最重要的,下文關(guān)于其具體介紹,需要的小伙伴可以參考一下
    2022-05-05
  • Java中的ThreadLocal與ThreadLocalMap詳解

    Java中的ThreadLocal與ThreadLocalMap詳解

    這篇文章主要介紹了Java中的ThreadLocal與ThreadLocalMap詳解,ThreadLocal 是一個線程局部變量,其實的功用非常簡單,就是為每一個使用該變量的線程都提供一個變量值的副本,是Java中一種較為特殊的線程綁定機(jī)制,需要的朋友可以參考下
    2023-09-09
  • Gradle的使用教程詳解

    Gradle的使用教程詳解

    Gradle它使用一種基于Groovy的特定領(lǐng)域語言(DSL)來聲明項目設(shè)置,目前也增加了基于Kotlin語言的kotlin-based DSL,拋棄了基于XML的各種繁瑣配置,下面通過本文給大家介紹Gradle的使用教程,感興趣的朋友一起看看吧
    2020-09-09

最新評論

麦盖提县| 巧家县| 诸城市| 疏附县| 苍梧县| 墨玉县| 博湖县| 积石山| 临猗县| 南平市| 宣恩县| 龙山县| 沾益县| 龙井市| 微山县| 鹤山市| 苍山县| 潍坊市| 宿迁市| 北安市| 河津市| 红河县| 汉川市| 松阳县| 防城港市| 东光县| 阜新| 光泽县| 望都县| 百色市| 柳林县| 神木县| 江油市| 九龙坡区| 赤壁市| 黑山县| 乌什县| 平度市| 黄浦区| 宁南县| 安图县|