關於springboot整合shiro的記錄

1.如果是默認使用username password 

常規配置文件pom依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jsdc</groupId>
    <artifactId>tianqi</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>tianqi</name>
    <description>this is my project</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- Spring Boot Web 依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!-- spring boot 默認的日誌框架是Logback,所以在引用log4j之前,需要先排除該包的依賴,再引入log4j2的依賴 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!-- Spring Boot Test 依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.jagregory</groupId>
            <artifactId>shiro-freemarker-tags</artifactId>
            <version>0.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-all</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.freemarker</groupId>
                    <artifactId>freemarker</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- Spring Boot Mybatis 依賴 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <!--MySql 驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.29</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.7</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

yaml文件

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/tianqi?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  freemarker:
    request-context-attribute: req
    suffix: .ftl
    content-type: text/html
    enabled: true
    template-loader-path: classpath:/templates
    charset: utf-8
logging:
  level:
    com.jsdc.tianqi: debug

 

shiro配置文件

package com.jsdc.tianqi.config;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.codec.Base64;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean(name = "shiroDbRealm")
    public SystemAuthorizingRealm getShiroDbRealm() {
        return new SystemAuthorizingRealm();
    }

    @Bean(name = "rememberMeCookie")
    public SimpleCookie getRememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * 這裏指定的是通過加鹽的方式來驗證密碼,注意下面的配置
     * @param systemAuthorizingRealm
     * @return
     */
    @Bean(name = "credentialsMatcher")
    public CredentialsMatcher getHashedCredentialsMatcher(SystemAuthorizingRealm systemAuthorizingRealm){
        //1
        // .參數SystemAuthorizingRealm必須有
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");//2.指定散列方式
        credentialsMatcher.setHashIterations(1);//3.散列次數
        systemAuthorizingRealm.setCredentialsMatcher(credentialsMatcher);//4.很關鍵的一步,要把驗證方式注入到自定義realm裏面去
        return credentialsMatcher;
    }


    @Bean(name = "rememberMeManager")
    public CookieRememberMeManager getRememberMeManager(SimpleCookie cookie) {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        cookieRememberMeManager.setCookie(cookie);
        return cookieRememberMeManager;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getSecurityManager(SystemAuthorizingRealm realm, CookieRememberMeManager rememberMeManager) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager);
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login.do");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login.do", "authc");
        filterChainDefinitionMap.put("/logout.do", "logout");
        filterChainDefinitionMap.put("/libs/**", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

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

 

自定義realm

package com.jsdc.tianqi.config;

import com.jsdc.tianqi.po.User;
import com.jsdc.tianqi.utils.PasswordUtils;
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.apache.shiro.util.ByteSource;
import org.apache.shiro.util.SimpleByteSource;

public class SystemAuthorizingRealm extends AuthorizingRealm {


    /**
     * 認證回調函數,登錄時調用.
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        //這裏有幾種方式:
        //1.update   token裏的password,將password加密
        //2.明文不需要操作
        //3.指定CredentialsMatcher 進行密碼比對


        //以下內容來自數據源查詢而來
        User user = new User();
        user.setId(1);
        user.setUsername("admin");
        user.setPassword("a6d4f0a9c109cd24eacb88e75e5be690");
        //這裏構造方法有幾個,比如需要配置salt,就用下面的構造方法
        // SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),
                user.getPassword(), ByteSource.Util.bytes(PasswordUtils.SALT), getName());
        return info;
    }

    /**
     * 授權查詢回調函數, 進行鑑權但緩存中無用戶的授權信息時調用
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        return null;
    }
}

控制器代碼

 /**
     * 登錄頁面
     */
    @RequestMapping(value = "login.do", method = RequestMethod.GET)
    public String login(){
        return "login";
    }

    @RequestMapping(value = "login.do", method = RequestMethod.POST)
    public String login(@RequestAttribute(required = false)String shiroLoginFailure) throws Exception {
        if (shiroLoginFailure != null) {
            if (UnknownAccountException.class.getName().equals(shiroLoginFailure)) {
                System.out.println("用戶名不存在!請確認用戶名和密碼後重新登錄");
            } else if (IncorrectCredentialsException.class.getName().equals(shiroLoginFailure)) {
                System.out.println("用戶名或密碼錯誤!請確認用戶名和密碼後重新登錄");
            } else {
                System.out.println("登錄失敗!請確認用戶名和密碼後重新登錄");
            }
        }
        return "login";
    }

這裏解釋一下:
shiro認證的流程,首先在配置文件指定loginurl,並對loginurL配置 FormAuthenticationFilter,簡稱authc
即過濾鏈中的filterChainDefinitionMap.put("/login.do", "authc");

然後,控制器登錄頁提交地址必須跟配置文件保持一致,當訪問login.do的時候,直接被filter攔截,並同realm做認證,如果認證成功了,會默認返回上一個頁面。(這裏也可以配置文件設置successUrl,包括失敗頁面,但是沒有必要)
就完成了登錄驗證。
也就說,登錄成功跳轉哪裏,交給shiro,login.do只需要判斷有沒有返回異常shiroLoginFailure,有進行提示,這個方法裏不做任何成功的跳轉。

2.如果是自定義userName,passWord

控制器代碼

 @RequestMapping(value = "login.do", method = RequestMethod.POST)
    public String login(String userName, String passWord, Boolean rememberMe) throws Exception {

      if(tqzlService.login(userName,passWord)){
          return "redirect:main";
      }
        return "redirect:login.do";
    }

service代碼

  public boolean login(String userName,String passWord){
        if(userName.equals("aa")){//這裏去數據庫查詢,如果user不存在 返回false.不然下面代碼會報錯500
                return false;
        }
        Subject subject = SecurityUtils.getSubject();
        // 利用Host屬性存放其他屬性,用逗號分隔保存,第二位是userId
        String host = "0.0.0.0," + 1;
        UsernamePasswordToken token = new UsernamePasswordToken(userName, passWord, true, host);
        subject.login(token);
        return true;
    }

認證回調也要判斷User是否存在,不存在直接return null 否則報錯

/**
     * 認證回調函數,登錄時調用.
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        //這裏有幾種方式:
        //1.update   token裏的password,將password加密
        //2.明文不需要操作
        //3.指定CredentialsMatcher 進行密碼比對
        if(token.getUsername().equals("aa")){//這裏去數據庫查詢,如果user不存在 返回false.不然下面代碼會報錯500
            return  null;
        }

        //以下內容來自數據源查詢而來
        User user = new User();
        user.setId(1);
        user.setUsername("admin");
        user.setPassword("1234");
        //這裏構造方法有幾個,比如需要配置salt,就用下面的構造方法
        // SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
        SimpleAuthenticationInfo info= new SimpleAuthenticationInfo(new ShiroUser(user.getId().toString(), user.getUsername()),
                user.getPassword(), getName());
        return info;
    }

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章