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类进行验证
代码没有放完整,源码地址:源码点这个

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