使用Shiro實現認證和受權(基於SpringBoot)

Apache Shiro是一個功能強大且易於使用的Java安全框架,它爲開發人員提供了一種直觀,全面的身份驗證,受權,加密和會話管理解決方案。下面是在SpringBoot中使用Shiro進行認證和受權的例子,代碼以下:java

pom.xml

導入SpringBoot和Shiro依賴:web

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.2</version>
        </dependency>
</dependencies>

也能夠直接導入Apache Shiro提供的starter:算法

<dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-web-starter</artifactId>
</dependency>

Shiro配置類

package com.cf.shiro1.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //設置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        //設置未認證(登陸)時,訪問須要認證的資源時跳轉的頁面
        shiroFilterFactoryBean.setLoginUrl("/loginPage");

        //設置訪問無權限的資源時跳轉的頁面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorizedPage");
        
        //指定路徑和過濾器的對應關係
        Map<String, String> filterMap = new HashMap<>();
        //設置/user/login不須要登陸就能訪問
        filterMap.put("/user/login", "anon");
        //設置/user/list須要登陸用戶擁有角色user時才能訪問
        filterMap.put("/user/list", "roles[user]");
        //其餘路徑則須要登陸才能訪問
        filterMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("realm") Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    @Bean
    public Realm realm() {
        MyRealm realm = new MyRealm();
        //使用HashedCredentialsMatcher帶加密的匹配器來替換原先明文密碼匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //指定加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        //指定加密次數
        hashedCredentialsMatcher.setHashIterations(3);
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        return realm;
    }
}

自定義Realm

package com.cf.shiro1.config;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class MyRealm extends AuthorizingRealm {
    /**
     * 受權
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Object username = principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(getRoles(username.toString()));
        return simpleAuthorizationInfo;
    }

    /**
     * 認證
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        String username = token.getUsername();
        Map<String, Object> userInfo = getUserInfo(username);
        if (userInfo == null) {
            throw new UnknownAccountException();
        }

        //鹽值,此處使用用戶名做爲鹽
        ByteSource salt = ByteSource.Util.bytes(username);

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, userInfo.get("password"), salt, getName());
        return authenticationInfo;
    }

    /**
     * 模擬數據庫查詢,經過用戶名獲取用戶信息
     *
     * @param username
     * @return
     */
    private Map<String, Object> getUserInfo(String username) {
        Map<String, Object> userInfo = null;
        if ("zhangsan".equals(username)) {
            userInfo = new HashMap<>();
            userInfo.put("username", "zhangsan");

            //加密算法,原密碼,鹽值,加密次數
            userInfo.put("password", new SimpleHash("MD5", "123456", username, 3));
        }
        return userInfo;
    }

    /**
     * 模擬查詢數據庫,獲取用戶角色列表
     *
     * @param username
     * @return
     */
    private Set<String> getRoles(String username) {
        Set<String> roles = new HashSet<>();
        roles.add("user");
        roles.add("admin");
        return roles;
    }
}

Controller

package com.cf.shiro1.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * 登陸
     * @param username
     * @param password
     * @return
     */
    @RequestMapping("/login")
    public String userLogin(String username, String password) {
        String result;

        //獲取當前用戶
        Subject currentUser = SecurityUtils.getSubject();

        //用戶是否已經登陸,未登陸則進行登陸
        if (!currentUser.isAuthenticated()) {
            //封裝用戶輸入的用戶名和密碼
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);

            try {
                //登陸,進行密碼比對,登陸失敗時將會拋出對應異常
                currentUser.login(usernamePasswordToken);
                result = "登陸成功";
            } catch (UnknownAccountException uae) {
                result = "用戶名不存在";
            } catch (IncorrectCredentialsException ice) {
                result = "密碼錯誤";
            } catch (LockedAccountException lae) {
                result = "用戶狀態異常";
            } catch (AuthenticationException ae) {
                result = "登陸失敗,請與管理員聯繫";
            }
        } else {
            result = "您已經登陸成功了";
        }

        return result;
    }

    @RequestMapping("/list")
    public String userList() {
        return "訪問我須要登陸而且須要擁有user角色!";
    }
}
相關文章
相關標籤/搜索