Spring中shiro的简单使用

Spring项目整合shiro

先将一下shiro工作的一个流程,代码示例在后面,想要直接看代码的朋友可以直接下滑。

shrio认证流程

1、通过配置文件创建 SecurityManager类。

2、用户登录时 创建 UsernamePasswordToken 类的一个对象(token)。

3、通过SecurityUtils.getSubject().login(token) 方法 提交认证。

4、SecurityManager 由 RealmAuthentication 进行认证。

5、RealmAuthentication 调用自定义的Realm对象(传入token)。

6、Real对象根据传入的token 根据账号查询用户信息。

​ 1> 查询到 -> 将用户信息返回。

​ 2> 否则返回null

7、RealmAuthentication 接收到realm 返回的信息

​ 1> null 抛出 unknownAccountException

​ 2> 找到用户(1、realm返回的密码与token对比 ,若不相同则返回IncorrectCredentialsException)

shiro授权流程

1、构建SecurityManager。

2、Subject.isPermitted()授权。

3、SecurityManager.isPermitted()、执行授权。

4、Authorizer 执行授权。

5、根据身份获取资源权限信息。

1、对Subject授权 调用isPermitted(“permission串”)方法。

2、SecurityManager执行授权–> 通过ModularRealmAuthorizer 执行。

3、ModularRealmAuthorizer 执行 自定义realm从数据库查询权限数据。调用realm的授权方法:doGetAuthorizationInfo()。

4、realm 将权限数据返回给RealmAuthorizer

5、RealmAuthorizer调用PermissionReslover 进行权限串对比。

6、对吧isPermitted中“permission串” 是否在realm查询的权限数据中。

代码示例

1、shiro配置类
@Configuration
public class ShiroConfig {

    // 1.配置 shiroFilter
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        //哪些请求可以匿名访问
        chain.addPathDefinition("/toLogin","anon");
        chain.addPathDefinition("/doLogin","anon");
        chain.addPathDefinition("/statics/**", "anon");

        return chain;
    }


    // 2. 配置SecurityManager
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        return securityManager;
    }

    // 3. 配置自定义的Realm
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        //给realm认证类,添加加密方式。加密原理:md5
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return userRealm;
    }

    // 3.1 配置salt加密
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//设置加密方式
        hashedCredentialsMatcher.setHashIterations(56);//加密循环的次数
        return hashedCredentialsMatcher;
    }

    // 4.初始化Shiro的生命周期
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**
     * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
     * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,
     * 导致返回404。加入这项配置能解决这个bug
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
//        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
}

2、创建自定义的Realm类
public class UserRealm extends AuthorizingRealm {

    @Resource
    private XxUserService xxUserService;

    @Resource
    private XxRoleService xxRoleService;

    @Resource
    private UserRoleService userRoleService;

    private static Logger logger = LoggerFactory.getLogger(UserRealm.class);


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("------------开始进入授权方法-------------");
        //获取登录用户名
        String userName = (String) principalCollection.getPrimaryPrincipal();

        //根据用户名去数据库查询用户信息
        XxUser xxUser  = xxUserService.findByUserName(userName);

        List<UserRole> userRoles = userRoleService.findByUserId(xxUser.getUserId());

        //角色列表
        List<String> roleNames = new ArrayList<>(userRoles.size());
        for (UserRole userRole : userRoles) {
            roleNames.add(xxRoleService.findByRoleId(userRole.getRoleId()).getRoleName());
        }
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRoles(roleNames);
        //同时,还和获取用户对应的权限集合
        //simpleAuthorizationInfo.addStringPermissions(new ArrayList<>());
        logger.info("------------授权成功!!-------------");
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //1.从token中获取用户名
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        String password = new String(token.getPassword());

        String md5Pwd = new SimpleHash("md5",password, ByteSource.Util.bytes(username),56).toHex();

        XxUser xxUser = xxUserService.findByUserName(token.getUsername());

        if (xxUser == null){
            throw new UnknownAccountException("账号不存在");
        }else{
            //用户存在则验证密码
            if ( !xxUser.getPassword().equals(md5Pwd)){
                throw new IncorrectCredentialsException("密码错误");
            }
        }

        return new SimpleAuthenticationInfo(xxUser, xxUser.getPassword(), ByteSource.Util.bytes(xxUser.getUserName()), getName());
    }


    //得到密码
    private static String getMd5Password(String password,String saltStr){
        String hashAlgorithmName = "MD5";//加密方式
        ByteSource salt = ByteSource.Util.bytes(saltStr);//以账号作为盐值
        int hashIterations = 56;// 加密56次(注意这里配置的值要与shiro的配置文件中配置的值保持一致。否则无法解密
        SimpleHash hash = new SimpleHash(hashAlgorithmName, password, salt, hashIterations);
        System.out.println(hash.toString());
        return hash.toString();
    }

    public static void main(String[] args) {
        getMd5Password("123456","admin");
    }
}

3、登录时检验

UsernamePasswordToken token = new UsernamePasswordToken((String)paramMap.get("userName"), (String)paramMap.get("password"));

/*
下面这段代码进行异常处理,若产生异常则表示登录失败。
*/
SecurityUtils.getSubject().login(token);//这一步操作成功,则表示用户登录成功

4、接口权限管理

上面提到已经将当前用户的角色和权限获取到。

在接口上使用注解

//对角色控制
@RequiresRoles(value = {"role1";"role2"},logical = Logical.OR)
//对权限控制
@RequiresPermissions(value = {"pre1";"pre2"},logical = Logical.AND)

如果有多个权限/角色验证的时候中间用“,”隔开,默认是所有列出的权限/角色必须同时满足才生效。但是在注解中有logical = Logical.OR这块。这里可以让权限控制更灵活些。

如果将这里设置成OR,表示所列出的条件只要满足其中一个就可以,如果不写或者设置成logical = Logical.AND,表示所有列出的都必须满足才能进入方法。

//需要登录
@RequiresAuthentication
//无需登录
@RequiresGuest

这有一篇博文,作者已经详细讲解了shiro关于权限控制的5个注解

http://blog.csdn.net/w_stronger/article/details/73109248

原创文章 7 获赞 2 访问量 395

猜你喜欢

转载自blog.csdn.net/bortherLiang/article/details/106115507