文章目录

  • 一、Shiro简历及组成
  • 二、Shiro过滤器&标签
  • 三、Shiro环境搭建
  • 1.添加shiro依赖
  • 2.配置web.xml
  • 3.创建自定义realm类:AuthRealm,继承AuthorizingRealm
  • 4.配置applicationContext-shiro.xml, Spring整合shiro
  • 四、Shiro登陆认证(一)认证实现
  • 1.修改LoginController,通过shiro实现登陆认证
  • 2.编写AuthRealm,实现登陆认证
  • 五、Shiro登陆认证(二)自定义凭证匹配器实现认证
  • 自定义凭证匹配器
  • 六、Shiro授权
  • 1. shiro授权校验有几种方式
  • 2.realm实现授权



提示:以下是本篇文章正文内容,下面案例可供参考

一、Shiro简历及组成

Apache Shiro是Java的一个安全框架。功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。

shiro的组成:
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情。
常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

subject.login(token);                            认证
subject.checkPermission("指定访问资源需要的权限")    授权

**Session Manager:**会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

**Cryptography:**加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

我们从外部来看Shiro吧,即从应用程序角度的来观察如何使用Shiro完成工作:

java项目免费授权方案 java授权认证_apache

可以看到:应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

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

Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
也就是说对于我们而言,最简单的一个Shiro应用:
	1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
	2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。

java项目免费授权方案 java授权认证_spring_02

二、Shiro过滤器&标签

过滤器

过滤器简称

对应的java类

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authc.UserFilter

logout

org.apache.shiro.web.filter.authc.LogoutFilter

标签

标签名称

标签条件(满足条件,才显示标签内容)

<shiro:authenticated >

登录之后

<shiro:notAuthenticated >

不在登录状态时

<shiro:guest >

用户在没有RememberMe时

<shiro:user >

用户在RememberMe时

<shiro:hasAnyRoles name=“abc,123” >

在有abc或者123角色时

<shiro:hasRole name=“abc”>

拥有角色abc

<shiro:lacksRole name=“abc”>

没有角色abc

<shiro:hasPermission name=“abc”>

拥有权限资源abc

<shiro:lacksPermission name=“abc”>

没有abc权限资源

<shiro:principal >

默认显示用户名称

三、Shiro环境搭建

搭建shiro环境并实现认证,步骤如下:

  1. 添加shiro依赖
  2. 配置web.xml
  3. 创建自定义realm类:AuthRealm,继承AuthorizingRealm
  4. 配置applicationContext-shiro.xml, Spring整合shiro
  5. 修改LoginController,通过shiro实现登陆认证
  6. 实现自定义realm类AuthRealm中的认证方法
  7. 登陆认证测试

1.添加shiro依赖

<!--shiro和spring整合-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!--shiro核心包-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
</dependency>

2.配置web.xml

<!--配置shiro的代理过滤器,拦截到请求后,把认证授权的处理交给容器中指定名称的bean-->
	<!--注意: 这里的filter-name 名称要匹配容器中的bean的名称-->
	<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

3.创建自定义realm类:AuthRealm,继承AuthorizingRealm

/**
 * 自定义的realm类,提供认证、授权数据访问的方法
 */
public class AuthRealm extends AuthorizingRealm{

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return null;
    }

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

}

4.配置applicationContext-shiro.xml, Spring整合shiro

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 创建过滤器的工厂,注入SecurityManager。注意:这里的id匹配web.xml的过滤器名称。-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--1.1 注入SecurityManager -->
        <property name="securityManager" ref="securityManager"/>

        <!--1.2 注入其他参数-->
        <!--1.2.1 认证失败后,默认跳转到的登陆页面-->
        <property name="loginUrl" value="/login.jsp"/>
        <!--1.2.2 认证成功后,默认跳转到的登陆页面(如果程序有指定,以程序指定为主)-->
        <property name="successUrl" value="/index.jsp"/>
        <!--1.2.3 授权校验失败,默认跳转的页面-->
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>

        <!--
            1.3 指定过滤器配置
             anon  匿名访问过滤器,指定需要放行的资源,这些资源不需要认证可以直接访问
             authc 认证过滤器,指定需要认证的资源
             perms 授权校验过滤器,指定需要进行权限校验的资源

            语法:
                /css/*   拦截css目录下的所有资源,不包含子目录
                /css/**  拦截css目录及其子目录的资源,包含子目录
                /login*  拦截login开头的请求路径
        -->
        <property name="filterChainDefinitions">
            <value>
                /css/** = anon
                /img/** = anon
                /make/** = anon
                /plugins/** = anon
                /login.jsp = anon
                /login.do = anon
                /index.jsp = anon
                /** = authc
            </value>
        </property>
    </bean>

    <!--2. 创建SecurityManager对象,注入realm-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
    </bean>

    <!--3. 创建自定义的realm,访问我们的认证、授权数据; 注入凭证匹配器-->
    <bean id="myRealm" class="cn.test.web.shiro.AuthRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!--4. 创建凭证匹配器,自动对用户输入的密码按照指定的算法加密-->
    <bean id="credentialsMatcher"
          class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
    </bean>
</beans>

四、Shiro登陆认证(一)认证实现

步骤

  1. 修改LoginController,通过shiro实现登陆认证
  2. 编写AuthRealm,实现登陆认证
  3. 测试

1.修改LoginController,通过shiro实现登陆认证

@Controller
public class LoginController extends BaseController {

    @Autowired
    private UserService userService;
    @Autowired
    private ModuleService moduleService;

    /**
     * shiro登陆认证
     */
    @RequestMapping("/login")
    public String login(String email,String password){
        if (StringUtils.isEmpty(email) || StringUtils.isEmpty(password)){
            return "forward:/login.jsp";
        }

        try {
            // 创建shiro的subject对象,表示当前的用户
            Subject subject = SecurityUtils.getSubject();
            // 创建token对象,封装账号密码信息
            AuthenticationToken token = new UsernamePasswordToken(email,password);
            // 登陆认证 (自动去到ream的认证方法)
            subject.login(token);

            // 认证成功
            // 获取认证后的身份对象(获取的是realm的认证方法返回的对象的构造函数的第一个参数)
            User user = (User) subject.getPrincipal();
            httpSession.setAttribute("loginUser",user);
            // 登陆成功,查询用户的权限
            List<Module> moduleList = moduleService.findModuleByUserId(user.getId());
            httpSession.setAttribute("moduleList",moduleList);
            // 登陆成功,跳转到主页
            return "home/main";
        } catch (AuthenticationException e) {
            e.printStackTrace();
            // 认证失败
            request.setAttribute("error","用户名或密码错误!");
            return "forward:/login.jsp";
        }
    }



    @RequestMapping("/home")
    public String home(){
        return "home/home";
    }

    // 注销
    @RequestMapping("/logout")
    public String logout(){
        // 释放资源
        httpSession.removeAttribute("loginUser");
        // 销毁服务器端的session
        httpSession.invalidate();
        // 注销后,跳转到登陆页面
        return "forward:/login.jsp";
    }
}

2.编写AuthRealm,实现登陆认证

/**
 * 自定义的realm类,提供认证、授权数据访问的方法
 */
public class AuthRealm extends AuthorizingRealm{

    @Autowired
    private UserService userService;

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 转换
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 获取登陆名
        String email = upToken.getUsername();
        // 根据email查询
        User user = userService.findByEmail(email);
        if (user == null) {
            return null;
        }
        // 获取数据库中正确的密码
        String dbPwd = user.getPassword();

        // 参数1:身份对象(从数据库中查询到的user对象)
        // 参数2:数据库中正确的密码
        // 参数3:realm的名称; 可以随便写,但是有多个realm要唯一。getName()获取默认名称
        SimpleAuthenticationInfo sai =
                new SimpleAuthenticationInfo(user,dbPwd,getName());
        return sai;
    }

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

}

五、Shiro登陆认证(二)自定义凭证匹配器实现认证

目前对用户输入的密码,我们直接按照md5加密。这样不安全,要对用户输入的密码加盐加密,确保安全性
最终使用:md5加密、加盐的方式

自定义凭证匹配器

/**
 * 自定义凭证匹配器,自己指定对用户输入的密码的加密方式
 * 加密方案: md5加密,用户名作为盐。
 */
public class CusomtCredentialsMatcher extends SimpleCredentialsMatcher {

    /**
     * 在凭证匹配器中,指定对用户输入的密码的加密方式,以及最终的校验
     * @param token 封装用户输入的密码、账号
     * @param info 封装认证后的身份对象 (拿到的是user对象,也就是数据库中正确的密码)
     * @return
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //1. 获取用户输入的email
        String email = (String) token.getPrincipal();
        //2. 获取用户输入的密码
        String inputPwd = new String((char[]) token.getCredentials());
        //3. 对用户输入的密码加密、加盐
        String encodePwd = new Md5Hash(inputPwd,email).toString();
        //4. 获取数据库中正确的密码(数据库的密码要加密、加盐。 注意:要先修改数据库再测试)
        String dbPwd = (String) info.getCredentials();
        //5. 对比:用户输入的密码加密加盐后,与数据库的密码进行对比
        return encodePwd.equals(dbPwd);
    }
}

applicationContext-shiro.xml配置自定义匹配器

<!--自定义的凭证匹配器,指定密码加密、加盐 -->
<bean id="credentialsMatcher" class="cn.test.web.shiro.CusomtCredentialsMatcher"></bean>

六、Shiro授权

1. shiro授权校验有几种方式

shiro提供了四种方式实现权限校验:

1) 硬编码方式

Subject subject = SecurityUtils.getSubject();
subject.checkPermission("部门管理");

2) 过滤器配置方式

/system/user/list.do = perms["用户管理"]

3) 注解方式

@RequiresPermissions(“”)

4) shiro提供的标签

<shiro:hasPermission name="用户管理">
    <a href="#">用户管理</a>
</shiro:hasPermission>

2.realm实现授权

实现自定义realm的doGetAuthorizationInfo()方法,返回用户已经具有的权限:

/**
 * 自定义的realm类,提供认证、授权数据访问的方法
 */
public class AuthRealm extends AuthorizingRealm{

    @Autowired
    private UserService userService;
    @Autowired
    private ModuleService moduleService;

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 转换
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 获取登陆名
        String email = upToken.getUsername();
        // 根据email查询
        User user = userService.findByEmail(email);
        if (user == null) {
            return null; // UnknownAccountException
        }
        // 获取数据库中正确的密码
        String dbPwd = user.getPassword();

        // 参数1:身份对象(从数据库中查询到的user对象)
        // 参数2:数据库中正确的密码
        // 参数3:realm的名称; 可以随便写,但是有多个realm要唯一。getName()获取默认名称
        SimpleAuthenticationInfo sai =
                new SimpleAuthenticationInfo(user,dbPwd,getName());
        return sai;
    }

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        /* 查询登陆用户的权限*/
        //1. 获取用户的身份对象(认证方法返回对象的构造函数的第一个参数)
        User user = (User) principals.getPrimaryPrincipal();

        //2. 根据用户的id查询权限
        List<Module> moduleList = moduleService.findModuleByUserId(user.getId());

        //3. 返回,告诉shiro当前登陆用户拥有的权限。后面就可以进行权限校验(四种方式)
        SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();
        if (moduleList != null && moduleList.size() > 0){
            for (Module module : moduleList) {
                // 返回用户的权限
                sai.addStringPermission(module.getName());
            }
        }
        return sai;
    }

}