SpringBoot2.2+shrio實現簡單的註冊,登陸驗證

1.什麼是shrio?

官方定義
在這裏插入圖片描述
意思是:
Apache Shrio是一個強大且使用簡單的Java加密框架,可以進行驗證,授權,加密和會話管理。Shrio有簡單且容易理解的API,你可以快速且容易的保護任何應用程序——小的移動應用程序和更大的企業級應用和web網站(純屬個人翻譯,沒有用第三方翻譯)
官網地址:http://shiro.apache.org/
源碼地址:項目源碼

2.什麼是Authentication(驗證)

shrio官方文檔提到了它的四個用處,先用一下Authentication做一個註冊和登陸驗證
看一下什麼Authentication
在這裏插入圖片描述
3.理解清楚概念,SpringBoot集成shrio,直接上代碼

  • 引入需要的shrio依賴

pom文件

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

註冊

-註冊需要注意的就是對用戶密碼加密處理
controller

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

    @Autowired
    private UserService userService;

    /**
     * @return
     * @author Ling
     * @Param
     * @Date 2020/2/28 11:18
     * @Description 用戶註冊
     */
    @PostMapping("/register")
    public Result register(@RequestBody User user) {
        Result result = new Result();
        if (null == user) {
            result.setResultCode(ResultCode.USER_USERINFO_NOT_BLANK);
        }
        result = userService.saveUser(user);
        return result;
    }
    }

Service

@Service
public class UserServiceImpl implements UserService {


    @Autowired(required = false)
    private UserDao userDao;



    @Override
    public Result saveUser(User user) {
        Result r = new Result();
        //判斷用戶是否爲新用戶
        String accout = user.getAccount();
        User userInfo = userDao.findUserInfoByAccount(accout);
        System.out.println("查詢結果爲" + userInfo);
        if(null != userInfo){
            r.setResultCode(ResultCode.USER_ALREADY_EXISTS);
        }else{
            //自定義加密類型對用戶密碼進行加密
            PwdUtils.pwdEncryption(user);
            //根據圖片命名,爲用戶隨機生成一個頭像
            int index = new Random().nextInt(6)+1;
            String userHeader = "static/header/user_"+index+".png";
            user.setUserHeader(userHeader);
            userDao.saveUser(user);
            r.setResultCode(ResultCode.SUCCESS);
        }

        return r;
    }

密碼加密,用的shrio自帶的加密方法SimpleHash

package com.ling.tools.login.utils;

import com.ling.tools.login.entity.User;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;

/**
 * File Name: PwdUtils
 * Date: 2020/2/28 15:09
 * Author: liangling
 * Description 用戶註冊密碼加密加鹽
 */
public class PwdUtils {

    private static final String algorithmName = "md5"; 
    private static final int hashIterations = 2;


    
 /**
  *  使用SimpleHash的構造器。四個參數
  *  algorithmName:要加密的算法名稱
  *  source:要加密的元數據,比如密碼
  *  salt:要一起加密的數據
  *  hashIterations: 代表hash迭代的次數,我設置的2,相當於md5(md5(""))
  */
    public static void pwdEncryption(User user){
       String salt = new SecureRandomNumberGenerator().nextBytes().toHex();//生成鹽
        user.setSalt(salt);
        String pwd = new SimpleHash(algorithmName,user.getPassword(), ByteSource.Util.bytes(user.getSalt()),hashIterations).toString();//生成密文
        user.setPassword(pwd);

    }
}

看了一眼源碼才知道的這四個參數
在這裏插入圖片描述

登陸驗證

1.自定義一個用戶信息驗證類

@Component
public class MyRealm extends AuthorizingRealm {


    @Autowired
    private UserDao userDao;


    /**
     * @return
     * @author
     * @Param
     * @Date 2020/2/6 16:57
     * @Des 驗證用戶名和密碼
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //自定義shrio判斷用戶名和密碼邏輯
        //1.判斷用戶是否存在

        String username = token.getPrincipal() + "";
        User user = userDao.findUserInfoByAccount(username);
        if (null == user) {
            throw new UnknownAccountException("用戶不存在");
        }
        //校驗用戶名密碼
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getAccount(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),
                getName());
        return authenticationInfo;
    }

編寫shrio配置文件,自定義密碼匹配憑證(自定義的密碼憑證必須和加密時候的算法名和次數一致,否則驗證不通過報錯),實例化安全管理器等

package com.ling.tools.login.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
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.springframework.aop.framework.DefaultAdvisorChainFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

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

/**
 * File Name: ShrioConfig
 * Date: 2020/2/6 15:08
 * Author: liangling
 * Description
 */
@Configuration
public class ShrioConfig {

  


     /**
       * 安全管理器
       */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(myRealm());
        return defaultWebSecurityManager;
    }




    /**
     * 憑證匹配器
     * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了
     *  所以我們需要修改下doGetAuthenticationInfo中的代碼;
     * )
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這裏使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次數,比如散列兩次,相當於 md5(md5(""));

        return hashedCredentialsMatcher;
    }

    @Bean
    public MyRealm myRealm(){
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myRealm;
    }

    /**
     *  開啓shiro aop註解支持.
     *  使用代理方式;所以需要開啓代碼支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
  

}

controller

package com.ling.tools.login.controller;

import com.ling.tools.login.entity.User;
import com.ling.tools.login.service.UserService;
import com.ling.tools.login.utils.Result;
import com.ling.tools.login.utils.ResultCode;
import jdk.nashorn.internal.parser.JSONParser;
import lombok.extern.slf4j.Slf4j;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
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.*;



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

    @Autowired
    private UserService userService;

    /**
     * @return
     * @author Ling
     * @Param
     * @Date 2020/2/28 11:18
     * @Description 用戶註冊
     */
    @PostMapping("/register")
    public Result register(@RequestBody User user) {
        Result result = new Result();
        if (null == user) {
            result.setResultCode(ResultCode.USER_USERINFO_NOT_BLANK);
        }
        result = userService.saveUser(user);
        return result;
    }

    @PostMapping("/login")
    public Result login(@RequestBody User user) {
        Result result = new Result();
        if(null == user){
            result.setResultCode(ResultCode.USER_USERINFO_NOT_BLANK);
        }
        executeLogin(user,result);
        return result;
    }


    private void executeLogin(User user,Result r) {
        //獲取一個subject對象用來調用login方法進行驗證
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(),user.getPassword());

        try {
            subject.login(token);
            r.setResultCode(ResultCode.SUCCESS);
        }catch (UnknownAccountException e){
            r.setResultCode(ResultCode.USER_DOSENOT_EXISTS);
        }catch (Exception e){
            r.setResultCode(ResultCode.FAIL);
        }

    }











}

整個驗證過程的入口就是從subject.login(token)開始的,源碼經過幾層調用最後會調用自定義的MyRealm類進行驗證
代碼沒有放完整,源碼地址:源碼點這個

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