SpringBoot整合Shiro MD5盐值加密

springboot DigestUtils md5加密_安全

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

Shiro的核心三大组件: Subject、SecurityManager 和 Realm

springboot DigestUtils md5加密_User_02

导入maven依赖

<!--整合shiro-->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.7.1</version>
</dependency>

配置类MyShiroConfig

配置类中需要在spring创建三个对象

  • ShiroFilterFactoryBean
  • SecurityManager
  • Realm
@Configuration
public class MyShiroConfig {

    @Bean
    public ShiroFilterFactoryBean filterFactory(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置默认登录的 URL,身份认证失败会访问该 URL
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 设置成功之后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/");
        // 设置未授权界面,权限认证失败会访问该 URL
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        // LinkedHashMap 是有序的,进行顺序拦截器配置
        Map<String, String> filterChainMap  = new LinkedHashMap<>();
        /*
               1). anon 可以被匿名访问
               2). authc 必须认证(登录)后才可以访问
               ......
        */
        filterChainMap.put("/login","anon");
        filterChainMap.put("/","anon");
        filterChainMap.put("/index","anon");
        filterChainMap.put("/logout","logout");
        filterChainMap.put("/**","authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilterFactoryBean;
    }

shiroFilterFactoryBean 需要注入的属性:

  • SecurityManager
  • 登录失败、成功、未授权时跳转页面的URL
  • 需要拦截或者放行的 URL:这些都放在一个 Map 中
    map的第一个参数为url,第二个参数为权限
@Bean
    public SecurityManager securityManager(Realm singleRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(singleRealm);
        return securityManager;
    }

需要为DefaultWebSecurityManager注入Realm对象

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

MyRealm是自己定义的,下面会介绍
需要进行MD5加密时,给AuthorizingRealm的CredentialsMatcher属性注入HashedCredentialsMatcher类型的对象,如下:

@Bean
    public HashedCredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //使用MD5加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        //加密的次数
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }
}

自定义Realm

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

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

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //将AuthenticationToken强转为UsernamePasswordToken
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //获取客户端传来的username
        String username = token.getUsername();

        //通过username查找数据库里
        User user = userService.getUserByUserName(username);

        //如果User为null则用户不存在
        if(User == null){
            throw new UnknownAccountException("用户不存在!");
        }
        //User.getStatus为freeze则用户账号被冻结
        if("freeze".equals(User.getStatus)){

            throw new LockedAccountException("用户被冻结");
        }

        /*

            登录输入用户名和密码,输入的密码会在shiro比较的时候通过MD5加密,
            然后再和SimpleAuthenticationInfo里传的数据库里的真实密码进行比较(且数据库里的真实密码是已经加密过的字符串)
         */


        //盐值:
        ByteSource byteSource = ByteSource.Util.bytes(username);
        //SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
        return new SimpleAuthenticationInfo(username, User.getPassword, byteSource, getName());
    }
}

Controller

@PostMapping("/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password){
        //获取Subject对象
        Subject subject = SecurityUtils.getSubject();

        //判断当前用户是否已经登录
        if(!subject.isAuthenticated()){
            //把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);

            //你可以自己设置一个标志位,然后根据这个标志位判断一下用户是否勾选了记住我,
            // 如果勾选了就使用 token.setRememberMe(true) 设置为记住我。
            token.setRememberMe(true);

            try {
                //进入到自定义的Realm类里的doGetAuthenticationInfo方法
                subject.login(token);
            } catch (AuthenticationException e) {
                System.out.println("登录失败:" + e.getMessage());
            }
        }
        return "index";
    }