Spring boot + shiro 基本运用记录

Apache Shiro,java项目的的安全框架,提供了认证、授权、加密、会话管理等..

Spring boot + shiro 基本运用记录_第1张图片
基本模块说明说明:

Authentication : 身份认证,登录
Authorization: 授权,权限验证
Session Management: 会话管理
Cryptongraphy: 加密
Subject : 主体,代表当前用户,与当前应用交互的任何东西都是Subject,所有subject都绑定到Security Manager
Security Manager : 安全管理器,shiro核心
Realm:域,shiro从realm 获取数据.

基本实现思路 :

1.导入依赖

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

2.继承 AuthorizingRealm,实现自己认证方式MyShiroRealm

/**
 * @author heyonghao
 * @Title: MyShiroRealm
 * @ProjectName xlkb2b
 * @Description: 验证,以及权限的添加MyShiroRealm.class
 *               实现AuthorizingRealm接口用户用户认证
 * @date 2019/3/27 002710:25
 */
public class MyShiroRealm extends AuthorizingRealm {
	//这是dao
    @Autowired
    ShiroLogin shiroLogin;

    //角色权限和对应权限添加
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //simpleAuthorizationInfo 用户角色权限管理
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        //获取登录用户名
        String sysname= (String) principals.getPrimaryPrincipal();
        //根据名称查询用户
        sysUser user = shiroLogin.findSysUserByName(sysname);
        //根据用户-角色码-查询对应-角色
        List<sysRole> roles = shiroLogin.findRoleById(user.getRcode());
        //添加角色,多角色时,添加list
        roles.forEach( r -> simpleAuthorizationInfo.addRole(r.getCaption()));
        //添加权限
        List<sysPermission> permissions = shiroLogin.findPermissById(user.getPcode());
        permissions.forEach(p -> simpleAuthorizationInfo.addStringPermission(p.getCaption()));

        return simpleAuthorizationInfo;
    }

    //用户认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
        //防止请求未到达时,就开始认证
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        sysUser user = shiroLogin.findSysUserByName(token.getUsername());
        //对比用户名
        if(StringUtil.objIsEmpty(user)){
            return null;
        }
        //盐
        String salt = user.getSalt();
        //用户输入-密码
        String oringnPassword = new String((char[]) token.getCredentials());
        //shiro加密-密码
        String encodedPassword = ShiroEncryption.getshiroPasswrod(oringnPassword,salt);
        if(!encodedPassword.equals(user.getPasswd())){
            throw new IncorrectCredentialsException("密码不正确");
        }
        return new SimpleAuthenticationInfo(user.getSysname(), oringnPassword, ByteSource.Util.bytes(salt) , getName());
    }


}

3.config配置

/**
 * @author heyonghao
 * @Title: ShiroConfiguration
 * @ProjectName xlkb2b
 * @Description: 过滤配置
 * shiro 基本配置说明:
 *               -     Subject          一个接口定义了很多认证授相关的方法,一个门面,与当前应用交互
 *               -     SecurityManager  shiro核心;接口;安全管理器,对全部的subject进行安全管理,继承了Authenticator, Authorizer, SessionManager这三个接口。从Realm获取数据
 *               -     Authenticator    认证器
 *               -     Authorizer       授权器
 *               -     realm            主要复制数据处理;兼部分认证授权校验
 * @date 2019/3/27 002710:52
 */
@Configuration
public class ShiroConfiguration {

    //将自己的验证方式加入容器
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String,String> map = new HashMap<>();
        //注意配置顺序
        map.put("/static/**", "anon");
        map.put("/favicon.ico", "anon");
        //登出
        map.put("/logout","logout");
        //对所有用户认证
        map.put("/**","authc");
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    //加入注解的使用,不加入这个注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /***
     * shiro的在进行密码验证的时候,将会在此进行匹配
     * @return
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("SHA-1");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(3);// 散列的次数,比如散列两次,相当于md5(md5(""));
        //hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);// 表示是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64;
        return hashedCredentialsMatcher;
    }

}

4.shiro密码生成配置

public class ShiroEncryption {

    /**
     * 对用户的密码进行MD5加密
     * @param password 密码
     * @param salt     盐
     * @return
     */
    public  static String getshiroPasswrod(String password,String salt){
        // shiro 自带的工具类生成salt
        //String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        // 加密次数
        int times = 3;
        // 算法名称
        String algorithmName = "SHA-1";
        String encodedPassword = new SimpleHash(algorithmName,password,salt,times).toString();
        // 返回加密后的密码
        return encodedPassword;
    }

}

这里在把mapper贴一下

@Component
@Mapper
public interface ShiroLogin {

    /**
     * 根据用户名,查询对应管理用户
     * @param sysname 用户名
     * @return
     */
    @Select({" SELECT suid,sysname,passwd,salt,rcode,pcode FROM sys_user WHERE sysname=#{sysname} "})
    sysUser findSysUserByName(@Param("sysname") String sysname);


    /**
     * 根据用户角色码,查询对应角色
     * @param codename  角色码
     * @return sysRoles 角色集合
     */
    @Select({" SELECT uid,caption FROM sys_role WHERE codename=#{codename} "})
    List<sysRole> findRoleById(@Param("codename") String codename);


    /**
     * 根据用户权限码,查询对应权限
     * @param codename     权限码
     * @return permissions 权限集合
     */
    @Select({" SELECT uid,caption FROM sys_permission WHERE codename=#{codename} "})
    List<sysPermission> findPermissById(@Param("codename") String codename);


    /**
     * 管理员用户新增
     * @param user 管理员用户
     * @return
     */
    @Insert({" INSERT INTO sys_user (suid,sysname,passwd,salt,rcode,pcode,usertype) value (#{suid},#{sysname},#{passwd},#{salt},#{rcode},#{pcode},#{usertype})  "})
    int saveSysUser(sysUser user);


}

5.登录controller (这里注意,在MyShiroRealm 用户认证的时候,抛出对应异常,这里捕捉!)

    //get请求,跳转登陆页
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String login(){
        return "shiro_login";
    }

    //post登录
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public String login(@RequestParam("name") String name, @RequestParam("password") String password , Model model){
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(name,password);
        //进行验证,这里可以捕获异常,然后返回对应信息
        try {
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
            model.addAttribute("message", "用户名错误");
            return "shiro_login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("message", "密码错误");
            return "shiro_login";
        }
        return "index";
    }

6.用户新增

/**
     * 用户添加
     * @return json
     */
    @RequestMapping(value = "/sysAdd" ,method = RequestMethod.POST)
    @ResponseBody
    public JSON sysAdd(@RequestBody  Map<String,String> paramMap){
        // shiro 自带的工具类生成salt
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        String passwd= ShiroEncryption.getshiroPasswrod(paramMap.get("password"),salt);//加密后的密码
        return loginService.addUser(paramMap.get("sysname"), passwd, salt, paramMap.get("rcode"), paramMap.get("pcode"),paramMap.get("usertype"));
    }

7.注销

 //登出
    @RequestMapping(value = "/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "forward:/login";
    }

特别提示: 在新增第一个用户的时候,可以把 --hashedCredentialsMatcher,密码匹配先注释掉,在数据添加个用户
         登录后,就可以正常新增,加密,加盐的 用户了!

你可能感兴趣的:(java安全框架)