天天看點

SpringBoot 整合Shiro 實作登入驗證攔截功能

目錄

前言:

概念:

源碼:

步驟:

(1) 首先我準備一個資料庫 shiro_demo 并且建立了一張bos_user 的表

(2)在pom 檔案中引入 Shiro 1.5.2 的jar 包

(3) 配置檔案中配置資料連接配接資訊

(4)編寫Shiro 的配置類

(5) 異常處理類  NyExceptionHandler

(6) MyFormAuthenticationFilter

(7) MyHashedCredentialsMatcher

(8) MyRealm

(9) MySessionManager

 (10) MyUserNamePasswordToken

(11)RedisSessionDao

 (12)整個項目路徑是這樣的!

測試:

(1) 我編寫了倆個接口:一個是test 一個是vueLogin

(2)業務邏輯類如下:

(3)在網頁上通路一下:http://localhost:8006/test/    滑鼠一回車顯示的是未登入

參考文章:

前言:

Shiro 安全架構是目前為止作為登入注冊最常用的架構,因為它十分的強大簡單,提供了認證,授權 ,加密和會話管理等功能。

我們項目的登入功能就內建了Shiro ,如果你也對Shiro感興趣,一起随着小編看下去吧!

SpringBoot 整合Shiro 實作登入驗證攔截功能

Shiro 官網 :http://shiro.apache.org/

Apache Shiro 1.5.2是目前的穩定版本(Java 1.8+ JVM)。

SpringBoot 整合Shiro 實作登入驗證攔截功能

概念:

圖引自:戳這

SpringBoot 整合Shiro 實作登入驗證攔截功能

最簡單的Shiro應用:

1.應用代碼通過Subject 來進行認證和授權,而Subject 又委托給SecurityManager

2.我們需要給Shiro的SecurityManager 注入Realm,進而讓SecurityManager能得到合法的使用者及其權限進行判斷。

三個核心元件:

Subject, SecurityManager 和 Realms.

①Subject:即“目前操作使用者”。但是,在Shiro中,Subject這一概念并不僅僅指人,也可以是第三方程序、背景帳戶(Daemon Account)或其他類似事物。它僅僅意味着“目前跟軟體互動的東西”。但考慮到大多數目的和用途,你可以把它認為是Shiro的“使用者”概念。Subject代表了目前使用者的安全操作,SecurityManager則管理所有使用者的安全操作。

②SecurityManager:它是Shiro架構的核心,典型的Facade模式,Shiro通過SecurityManager來管理内部元件執行個體,并通過它來提供安全管理的各種服務。

③Realm: Realm充當了Shiro與應用安全資料間的“橋梁”或者“連接配接器”。也就是說,當對使用者執行認證(登入)和授權(通路控制)驗證時,Shiro會從應用配置的Realm中查找使用者及其權限資訊。

原文:https://www.jianshu.com/p/6abc22ec3cb8

Shiro登入驗證的流程:

  1. 調用subject.login() 方法進行登入,其會自動委托給securityManager.login方法進行登入。
  2. 通過SecurityManager進入 realm 中進行判斷權限和登入認證
  3. 在 realm 中調用dao 層查詢資料庫
  4. 擷取使用者資料
  5. realm 包裝原始資料傳入SecurityManager。通過SecurityManager調用認證方法,根據login 傳入的 token 和 realm 傳回的使用者真實資料進行比對是否正确。如果不正确抛出各種異常,比如未知使用者異常,密碼錯誤,登入次數過多異常等。
  6. 根據傳回的異常,各種 try..catch..,沒有異常傳回,則表示登入成功,反之傳回前端 catch 結果。

Shiro 登入認證過程詳解:https://blog.csdn.net/caoyang0105/article/details/82769293

1. Shiro認證目的就是判斷使用者的賬号密碼是否正确之類的,授權的作用嘛,簡單的解釋就是給使用者賦予相應的權限。

2. 判斷一個使用者有沒有權限通路,無非是攔截URL能否通路,分為以下幾個操作

  • 判斷目前URL是否需要攔截,不需要則放行,需要則進入下一步
  • 判斷使用者是否已登陸,有則下一步,無則抛出401未認證
  • 從資源表/權限表讀取目前登陸使用者的可通路位址,進入下一步
  • 判斷是否包含使用者目前通路的位址,有則放行,無則抛出403無權限

源碼:

連結:https://pan.baidu.com/s/1DxfE-Azg2RZrSS6sDlP59Q 

提取碼:01z2 

       同時代碼也放入github中,網址:https://github.com/tanghh0410/csdn_blog.git 

SpringBoot 整合Shiro 實作登入驗證攔截功能

步驟:

(1) 首先我準備一個資料庫 shiro_demo 并且建立了一張bos_user 的表

SpringBoot 整合Shiro 實作登入驗證攔截功能

(2)在pom 檔案中引入 Shiro 1.5.2 的jar 包

<!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.5.2</version>
        </dependency>
           

(3) 配置檔案中配置資料連接配接資訊

server.port=8006
# MySQL Database
spring.datasource.url=jdbc:mysql://localhost:3306/shiro_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

shiro.jessionid=sessionId
           

(4)編寫Shiro 的配置類

package com.example.shiro.config;


import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.servlet.HandlerExceptionResolver;

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

/**
 *
 */
@Configuration
public class ShiroConfig {
    @Value("${shiro.jessionid}")
    private String jessionId;
    @Value("${spring.datasource.url}")
    public String url;
    @Value("${spring.datasource.username}")
    public String username;
    @Value("${spring.datasource.password}")
    public String password;
    @Value("${spring.datasource.driver-class-name}")
    public String driver;

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        /* 自定義filter注冊 */
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("myAuthc", new MyFormAuthenticationFilter());

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //注意過濾器配置順序 不能颠倒
        //配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實作了,登出後跳轉配置的loginUrl

        // 配置不會被攔截的連結 順序判斷
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/vueLogin", "anon");

        filterChainDefinitionMap.put("/**", "myAuthc");
        //轉應配置shiro預設登入界面位址,前後端分離中登入界面跳由前端路由控制,背景僅傳回json資料
        shiroFilterFactoryBean.setLoginUrl("/unauth");
        //未授權界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public MyRealm realm() {
        MyRealm myRealm = new MyRealm();
        //驗證
        MyHashedCredentialsMatcher myHashedCredentialsMatcher = new MyHashedCredentialsMatcher();
        myHashedCredentialsMatcher.setHashAlgorithmName("MD5");
        myRealm.setCredentialsMatcher(myHashedCredentialsMatcher);
        return myRealm;
    }

//    @Bean
//    public JdbcRealm jdbcRealm() {
//        JdbcRealm jdbcRealm = new JdbcRealm();
//        jdbcRealm.setPermissionsLookupEnabled(true);//為了查詢權限表,要開啟權限查詢
//        jdbcRealm.setDataSource(datasource());
//        return jdbcRealm;
//    }


    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        // 自定義緩存實作 使用redis
        // 自定義session管理
        securityManager.setSessionManager(defaultWebSessionManager());
        return securityManager;
    }

    @Bean
    public RedisSessionDao getRedisSessionDao() {
        return new RedisSessionDao();
    }

    /**
     * @return
     * @see DefaultWebSessionManager
     */
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager defaultWebSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //sessionManager.setCacheManager(cacheManager());
        //12小時
        sessionManager.setGlobalSessionTimeout(43200000);
        sessionManager.setDeleteInvalidSessions(true);
        //關鍵在這裡
        sessionManager.setSessionDAO(getRedisSessionDao());
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionIdCookie(getSessionIdCookie());
        return sessionManager;
    }

    /**
     * 給shiro的sessionId預設的JSSESSIONID名字改掉
     *
     * @return
     */
    @Bean(name = "sessionIdCookie")
    public SimpleCookie getSessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(jessionId);
        return simpleCookie;
    }

    /**
     * 自定義sessionManager
     *
     * @return
     */
    @Bean
    public SessionManager sessionManager() {
        MySessionManager mySessionManager = new MySessionManager();
        return mySessionManager;
    }

    /**
     * 開啟aop注解支援
     *
     * @param
     * @return
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類,并在必要時進行安全邏輯驗證
     * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator(可選)和AuthorizationAttributeSourceAdvisor)即可實作此功能
     *
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name = "exceptionHandler")
    public HandlerExceptionResolver handlerExceptionResolver() {
        return new MyExceptionHandler();
    }

}
           

(5) 異常處理類  NyExceptionHandler

package com.example.shiro.config;


import com.alibaba.fastjson.support.spring.FastJsonJsonView;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: tanghh18
 * @Date: 2020/4/6 11:28
 */
public class MyExceptionHandler implements HandlerExceptionResolver {
    private static Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class);
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
        ModelAndView mv = new ModelAndView();
        FastJsonJsonView view = new FastJsonJsonView();
        Map map = new HashMap();
        if (ex instanceof UnauthenticatedException|| ex instanceof IncorrectCredentialsException || ex instanceof AuthenticationException) {
            map.put("msg", "使用者名或密碼錯誤");
        } else if (ex instanceof UnauthorizedException) {
            map.put("msg", "無權限");
        } else {
            map.put("msg", ex.getMessage());
            logger.info(ex.getMessage());
            ex.printStackTrace();
        }
        view.setAttributesMap(map);
        mv.setView(view);
        return mv;
    }
}
           

(6) MyFormAuthenticationFilter

package com.example.shiro.config;

import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.context.annotation.Bean;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * @Author: tanghh18
 * @Date: 2020/4/6 11:29
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected AuthenticationToken createToken(String username, String password, ServletRequest request, ServletResponse response) {
        boolean rememberMe = isRememberMe(request);
        String host = getHost(request);
        String loginType = "password";
        if(request.getParameter("loginType")!=null && !"".equals(request.getParameter("loginType").trim())){
            loginType = request.getParameter("loginType");
        }
        return new MyUsernamePasswordToken(username, password,loginType,rememberMe,host);
    }
}
           

(7) MyHashedCredentialsMatcher

package com.example.shiro.config;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;


/**
 * @Author: tanghh18
 * @Date: 2020/4/6 11:30
 */
public class MyHashedCredentialsMatcher extends HashedCredentialsMatcher {
    @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
       MyUsernamePasswordToken mupt = (MyUsernamePasswordToken)token;
        if ("nopassword".equals(mupt.getLoginType())) {
            return true;
        }else {
            return super.doCredentialsMatch(token, info);
        }
    }
}

           

(8) MyRealm

package com.example.shiro.config;

import com.example.model.BosUserModel;
import com.example.service.BosUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * @Author: tanghh18
 * @Date: 2020/4/6 11:34
 */
public class MyRealm extends AuthorizingRealm {
    @Autowired
    BosUserService bosUserService;

    /**
     * 授權
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /**
     * 認證
     *
     * @param arg0
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
        //将父類的AuthenticationToken強轉為UsernamePasswordToken(因為需要調用UsernamePasswordToken的方法)
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) arg0;
        //擷取使用者名
        String uname = usernamePasswordToken.getUsername();
        //模拟通過資料庫查詢到使用者名的密碼
        BosUserModel user = bosUserService.findUserModelByName(uname);
        //認證資訊(參數填寫的是使用者輸入的使用者名和資料庫查詢的密碼)
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(uname, user.getPassword(), getName());
        return info;
    }
}
           

(9) MySessionManager

package com.example.shiro.config;

import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

/**
 * @Author: tanghh18
 * @Date: 2020/4/6 14:00
 */
public class MySessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "Authorization";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        //如果請求頭中有 Authorization 則其值為sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否則按預設規則從cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}
           

 (10) MyUserNamePasswordToken

package com.example.shiro.config;

import org.apache.shiro.authc.UsernamePasswordToken;


/**
 * @Author: tanghh18
 * @Date: 2020/4/6 14:00
 */
public class MyUsernamePasswordToken extends UsernamePasswordToken {
    /**
     * 免密登入/賬号密碼登入
     */
    private String loginType;

    public MyUsernamePasswordToken() {
        super();
    }
    /**
     * 賬号密碼登入
     * @param username
     * @param password
     * @param loginType
     * @param rememberMe
     * @param host
     */
    public MyUsernamePasswordToken(String username, String password, String loginType, boolean rememberMe,  String host) {
        super(username, password, rememberMe, host);
        this.loginType = loginType;
    }

    /**
     * 免密登入
     * @param username
     */
    public MyUsernamePasswordToken(String username) {
        super(username, "", false, null);
        this.loginType = "nopassword";
    }

    public MyUsernamePasswordToken(String username, String password) {
        super(username, password, false, null);
        this.loginType ="password";
    }

    public String getLoginType() {
        return loginType;
    }

    public void setLoginType(String loginType) {
        this.loginType = loginType;
    }
}
           

(11)RedisSessionDao

package com.example.shiro.config;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Created by Administrator on 2018/7/31.
 */
@Service
public class RedisSessionDao extends AbstractSessionDAO {
    /**
     *  Session逾時時間,機關為毫秒
     */
    private static Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
    private long expireTime = 30*60*1000;
    /**
     * Redis操作類,對這個使用不熟悉的,可以參考前面的部落格
     */
    @Autowired
    private RedisTemplate redisTemplate;

    public RedisSessionDao() {
        super();
    }

    public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) {
        super();
        this.expireTime = expireTime;
        //Long類型不可以會出現異常資訊;
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        this.redisTemplate = redisTemplate;
    }

    /**
     * 更新session
     * @param session
     * @throws UnknownSessionException
     */
    @Override
    public void update(Session session) throws UnknownSessionException {
        if (session == null || session.getId() == null) {
            return;
        }
        session.setTimeout(expireTime);
        redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
    }

    /**
     * 删除session
     * @param session
     */
    @Override
    public void delete(Session session) {
        if (null == session) {
            return;
        }
        redisTemplate.opsForValue().getOperations().delete(session.getId());
    }


    /**
     * // 擷取活躍的session,可以用來統計線上人數,如果要實作這個功能,
     * 可以在将session加入redis時指定一個session字首,統計的時候則使用keys("session-prefix*")的方式來模糊查找redis中所有的session集合
     * @param
     * @return
     */
    @Override
    public Collection<Session> getActiveSessions() {
        Set keys = redisTemplate.keys("*");
        Collection<Session> activeSessions = new ArrayList<>();
        for (Object o:keys){
            String sessionId = o.toString();
            if(sessionId.equals("SuiteTicket") || sessionId.equals("suite_access_token")){
                continue;
            }
            Session session = (Session) redisTemplate.opsForValue().get(sessionId);
            activeSessions.add(session);
        }
        return activeSessions;
    }

    /**
     * 加入session
     * @param session
     * @return
     */
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        //Long類型不可以會出現異常資訊;
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
        return sessionId;
    }

    /**
     * 讀取session
     * @param sessionId
     * @return
     */
    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (sessionId == null) {
            return null;
        }
        return (Session) redisTemplate.opsForValue().get(sessionId);
    }

    public long getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(long expireTime) {
        this.expireTime = expireTime;
    }

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        //Long類型不可以會出現異常資訊;
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        this.redisTemplate = redisTemplate;

    }
}
           

 (12)整個項目路徑是這樣的!

SpringBoot 整合Shiro 實作登入驗證攔截功能

測試:

(1) 我編寫了倆個接口:一個是test 一個是vueLogin

package com.example.controller;


import com.example.model.BosUserModel;
import com.example.service.BosUserService;
import com.example.shiro.config.MyUsernamePasswordToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: wwc
 * @Date: 2019/3/6 14:26
 * @Version 1.0
 */
@RestController
public class ShiroController {

    @Autowired
    private BosUserService bosUserService;


    @GetMapping(value = "/test")
    public void test(){
        System.out.println("這是測試接口-----");
    }


    /**
     * 登入方法
     *
     * @return
     */
    @GetMapping(value = "/vueLogin")
    public void login(HttpSession session) throws Exception {

        //shiro登入,普通的使用者名和密碼登入
        Subject subject = SecurityUtils.getSubject();
        BosUserModel user = new BosUserModel();
        user.setId(1);
        user.setUserName("soup_tang");
        user.setPassword("1");
//        user.setPassword("c4ca4238a0b923820dcc509a6f75849b");
        UsernamePasswordToken token = new MyUsernamePasswordToken(user.getUserName(), user.getPassword());
        subject.login(token);
        //使用者資料儲存到session裡
        BosUserModel bosUserModel = bosUserService.findUserModelByName(user.getUserName());
        if(bosUserModel!=null){
            System.out.println("登入成功");
            session.setAttribute("user", bosUserModel);
        }else{
            System.out.println("登入失敗");
        }
    }


    /**
     * 未登入,shiro應重定向到登入界面,此處傳回未登入狀态資訊由前端控制跳轉頁面
     *
     * @return
     */
    @RequestMapping(value = "/unauth")
    @ResponseBody
    public Object unauth() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("msg", "未登入");
        return map;
    }
}
           

(2)業務邏輯類如下:

package com.example.service.impl;

import com.example.model.BosUserModel;
import com.example.service.BosUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author tanghh
 * @Date 2020/4/6 10:49
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class BosUserServiceImpl implements BosUserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    /**
     * 根據名字查詢使用者資料
     * @param name
     * @return
     */
    @Override
    public BosUserModel findUserModelByName(String name) {
        String sql = "select * from bos_user where user_name=?";
        Map map = new HashMap<>();
        map.put("name",name);
        BosUserModel userModel = this.jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<BosUserModel>(BosUserModel.class),name);
        return userModel;
    }
}
           

(3)在網頁上通路一下:http://localhost:8006/test/    滑鼠一回車顯示的是未登入

SpringBoot 整合Shiro 實作登入驗證攔截功能

 (4)在網頁上通路一下:http://localhost:8006/vueLogin/

SpringBoot 整合Shiro 實作登入驗證攔截功能

控制台列印語句:

SpringBoot 整合Shiro 實作登入驗證攔截功能

一個接口test 被攔截了  , 一個vueLogin沒有被攔截,那麼是通過什麼方法被攔截的呢?答案在:ShiroConfig類。

我在下面這個類的位置 配置了 vueLogin 不被攔截。

SpringBoot 整合Shiro 實作登入驗證攔截功能

 還有一個問題就是:

可以看到下面這張圖我将 密碼寫成的是md5加密的值,這個時候會出現什麼問題呢,往下看:

SpringBoot 整合Shiro 實作登入驗證攔截功能

在我的  MyExceptionHandler 類開始報異常了。

SpringBoot 整合Shiro 實作登入驗證攔截功能

百度一下結果 是因為傳過來的密碼無法将值hash ,後面将代碼改過來就可以了,因為我在下面這個方法中寫了将密碼轉化成md5

SpringBoot 整合Shiro 實作登入驗證攔截功能

使用: user.setPassword("1");

參考文章:

Shiro學習目錄貼:https://www.iteye.com/blog/jinnianshilongnian-2018398

繼續閱讀