Apache下的shiro安全框架

1.介绍
它是:Apache下的一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能。

2.学习
这里只介绍怎么去使用,大致学习可以在这个地址:
https://www.w3cschool.cn/shiro/andc1if0.html

3.基本功能点图
在这里插入图片描述
4.Subject主题
主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager。

5.SecurityManager
安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器。

6.Realm
域;Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

1)应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager。

2)我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

7.授权方式
1)编程式

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
    //有权限
} else {
    //无权限
}

2)注解式

@RequiresRoles("admin")
public void hello() {
    //有权限
}

3)标签式

<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>

8.使用
1)建立自己定义的表用户表、角色表、用户角色表、权限表、角色权限表,实现方式不一样也可以根据自己的方式建表
在这里插入图片描述
2)在springboot中集成shiro依赖

<!--shiro-->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.4.0</version>
</dependency>
<!--shiro and spring-->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.0</version>
</dependency>

3)在UserService中加入方法,能通过登录名查询用户、角色、权限数据的方法

@Override
public SysUser findByUsername(String username) {
    return userMapper.findByUsername(username);
}

4)自定义realm类(shiro安全数据源)

package com.itgo.springboot.shiro;
import com.itgo.springboot.entity.SysPermission;
import com.itgo.springboot.entity.SysRole;
import com.itgo.springboot.entity.SysUser;
import com.itgo.springboot.service.UserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

//自定义shiro的安全数据源Realm
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUser user = (SysUser) principals.fromRealm(this.getClass().getName()).iterator().next();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if(null == user){
            return null;
        }
        //给特定的用户所以权限(如超级管理员)
        if(user.getId().equals("SUPER")){
            info.addRole("SUPER");
            info.addStringPermission("*:*:*");
            return info;
        }
        List<String> permissionList = new ArrayList<>();
        List<String> roleNameList = new ArrayList<>();

        List<SysRole> roleList = user.getRoles();
        if (CollectionUtils.isNotEmpty(roleList)) {
            for(SysRole role : roleList) {
                roleNameList.add(role.getCode());
                List<SysPermission> permissions = role.getPermissions();
                if (CollectionUtils.isNotEmpty(permissions)) {
                    for (SysPermission permission : permissions) {
                        permissionList.add(permission.getCode());
                    }
                }
            }
        }
        info.addRoles(roleNameList);
        info.addStringPermissions(permissionList);
        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //doGetAuthenticationInfo 获取身份验证相关信息:首先根据传入的用户名获取 User 信息;然后如果 user 为空,
        //那么抛出没找到帐号异常 UnknownAccountException;如果 user 找到但锁定了抛出锁定异常 LockedAccountException;
        //最后生成 AuthenticationInfo 信息,交给间接父类 AuthenticatingRealm 使用 CredentialsMatcher 进行判断密码是否匹配,
        //如果不匹配将抛出密码错误异常 IncorrectCredentialsException;另外如果密码重试此处太多将抛出超出重试次数异常 ExcessiveAttemptsException;
        //在组装 SimpleAuthenticationInfo 信息时,需要传入:身份信息(用户名)、凭据(密文密码)、盐(username+salt),
        //CredentialsMatcher 使用盐加密传入的明文密码和此处的密文密码进行匹配。
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        SysUser user = userService.findByUsername(username);
        //用户找不到异常
        if(null == user){
            throw new UnknownAccountException();
        }
        //账号是否锁定,在数据库里面定义其状态
        //TODO
        if("".equals("")){
            throw new LockedAccountException();
        }
        ByteSource salt = ByteSource.Util.bytes(user.getUsername() + user.getSalt());
        SimpleAuthenticationInfo  authorizationInfo = new SimpleAuthenticationInfo (
                user,                               //用户对象
                user.getPassword(),                 //数据库中密码
                salt,                               //salt(盐) = username(用户名,也可以是其他) + mysalt(自己定义的盐,不要让别人知道)
                this.getClass().getName());         //自己定义的realm
        return authorizationInfo;
    }
}

5)定义spirngboot的shiro配置类

package com.itgo.springboot.config;

import com.itgo.springboot.shiro.CredentialMatcher;
import com.itgo.springboot.shiro.MyRealm;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
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.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

//shiro核心配置,shrio执行过程
@Configuration
public class ShiroConfiguration {

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);

        bean.setLoginUrl("/login");                //登录页面
        bean.setSuccessUrl("/index");              //登录成功调整页面
        bean.setUnauthorizedUrl("/unauthorized");  //权限认证失败跳转页面

        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index", "authc");            //必须认证
        filterChainDefinitionMap.put("/login", "anon");             //不用认证,就是放开
        filterChainDefinitionMap.put("/loginUser", "anon");         //不用认证
        filterChainDefinitionMap.put("/admin", "roles[admin]");     //必须是admin的角色才能通过
        filterChainDefinitionMap.put("/edit", "perms[edit]");       //必须有edit的权限才能操作
        filterChainDefinitionMap.put("/druid/**", "anon");          //不用认证
        filterChainDefinitionMap.put("/**", "user");                //其他的接口,多需要登录
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return bean;
    }

    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("authRealm") MyRealm authRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        return manager;
    }

    @Bean("authRealm")
    public MyRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) {
        MyRealm authRealm = new MyRealm();
        authRealm.setCacheManager(new MemoryConstrainedCacheManager());
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

    @Bean("credentialMatcher")
    public CredentialMatcher credentialMatcher() {
        return new CredentialMatcher();
    }


    //shiro与spring进行关联的bean
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

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

5)定义密码校验CredentialMatcher继承SimpleCredentialsMatcher

package com.itgo.springboot.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.codec.CodecSupport;

//密码校验规则类
public class CredentialMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //通过token拿到明文密码
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //通过info拿到数据库密码及盐
        SimpleAuthenticationInfo simpleInfo = (SimpleAuthenticationInfo) info;
        // 获取明文密码
        String password = String.valueOf(usernamePasswordToken.getPassword());
        //密码盐
        String salt = CodecSupport.toString(simpleInfo.getCredentialsSalt().getBytes());
        //数据库密码
        String  dbPassword = (String)simpleInfo.getCredentials();
        return this.equals(ShiroUtil.encrypt(password,salt),dbPassword);
    }
}

6)定义测试控制器

package com.itgo.springboot.controller;

import com.itgo.springboot.entity.SysUser;
import com.itgo.springboot.service.UserService;
import com.itgo.springboot.shiro.ShiroUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

@Controller
public class TestController {

    @Resource
    private UserService userService;

    @RequestMapping("/login")
    public String loginUser(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            HttpSession session) {
        //通过密码和用户 组装toen
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //拿到用户主题
        Subject subject = SecurityUtils.getSubject();
        try {
            //进行认证,这里会去数据库
            subject.login(token);
            SysUser user = (SysUser) subject.getPrincipal();
            session.setAttribute("user", user);
            return "index";
        } catch (UnknownAccountException e) {
            //密码错误
            return "login";
        }catch (LockedAccountException e){
            //账号被锁定
            return "login";
        }catch (IncorrectCredentialsException e){
            //密码错误
            return "login";
        }catch (ExcessiveAttemptsException e){
            //重试数据过多
            return "login";
        }
    }


    /**
     * 添加用户
     * @param user 实体对象
     */
    @PostMapping("/save")
    //@RequiresPermissions({"system:user:add", "system:user:edit"})
    public String save(SysUser user) {
        // 对密码进行加密
        //ShiroUtil.getRandomSalt() 之后可以用这个生成
        String salt = user.getUsername() + user.getSalt();
        String nowPassword = ShiroUtil.encrypt(user.getPassword(),salt);
        user.setPassword(nowPassword);
        user.setSalt(salt);

        // 保存数据
        userService.save(user);
        return "user";
    }
}

9.问题
1)在集成shiro的rememberMe记住我时出现,必须让实体类实现序列化
在这里插入图片描述
在这里插入图片描述
2)在集成shiro的rememberMe是加密过程报无法初始化密码错误,解决办法让传入参数为长度16 byte[]
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值