shiro框架二:基于URL粗粒度的用户登录

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/java_xxxx/article/details/81672513

今天继续学习shiro框架,在基于昨天的基础之上,来进行用户的登录,如果登录成功,则跳转到index.html页面。否则就一直在login.html页面。
上篇播客写到了,项目打开后只能停留在login.html页面,其他页面是不许与登录的,因为在spring-shiro.xml文件中配置了权限authc:为登录无权访问。

好了,废话不多说,今天实现用户等路功能,遇到很多bug。
首先,流产个很重要,有了流程我们才能顺着思路去写代码,Shiro 执行流程, 应用程序 — Subject — SecurityManager — Realm 安全数据。这里解释一下,subject–>SecurityManager是Apache封装好的后台代码,subject会自动去找SecurityManager,大家不要在这里有断层。

应用程序,当然就是我们的controller了,接受请求。
直接上代码

整体思路

// 思路:
        *** controller层
        // 1、创建一个Subject对象
        // 2、将前端请求数据(这里是登录,就是用户名和密码)放到UsernamePasswordToken里
        // 3、Subject.login();  // 该方法会自动去找SecurityManager
        *** 自定义realm
        // 4、然后SecurityManager里的的用我们自己的realm实现类
        // 5、在自定义的realm里请求service层,请求数据,
        // 6、SimpleAuthenticationInfo类的构造方法有3个参数 shiroUser,shiroUser.getPassword(),getName()
        //    6.1:shiroUser 是数据库返回的数据
        //    6.2:shiroUser.getPassword() 从数据库中查询出的密码
        //    6.3:getName()  该方法会自动获得realm的名称
        //    6.4 :第6步的思路:realm类中有两个方法,一个是授权,一个是认证,我们的登录当然是在认证中了,认证之后,才能进入系统,然后                 进行授权。SimpleAuthenticationInfo类中,如果登录成功,即我们的sql语句是按用户的姓名得到的用户,所以姓名没有问题,然后SimpleAuthenticationInfo会根据从数据库中查询出来的密码和用户数据的密码来比较,如果正确则登录成功,否则登录失败。登陆成功之后,我们的认证方法会返回3个返回值(即SimpleAuthenticationInfo的构造函数的3个参数),其中的第一个参数就是我们在数据库中查到的user对象,该方法会将数据返回给subject,此时subject中就有了我们在数据库中查到的user对象(在授权方法里会用到该数据),这个subject也是我们controller在登录的时候所传递过来的那个subject。controller穿过来用户subject(用户的登录信息在这里放着,然后拿到subject中的信息去数据库中查询,如果登录成功就将一些数据保存在subject中,然后我们就可以拿着这个subject中的数据,去进行授权等操作)

controller

@RequestMapping("/user/login.do")
    @ResponseBody
    public ApiResult login(@RequestBody ShiroUser user){

        // 思路:
        *** controller层
        // 1、创建一个Subject对象
        // 2、将前端请求数据(这里是登录,就是用户名和密码)放到UsernamePasswordToken里
        // 3、Subject.login();  // 该方法会自动去找SecurityManager
        *** 自定义realm
        // 4、然后SecurityManager里的的用我们自己的realm实现类
        // 5、在自定义的realm里请求service层,请求数据,
        // 6、SimpleAuthenticationInfo类的构造方法有3个参数 shiroUser,shiroUser.getPassword(),getName()
        //    6.1:shiroUser 是数据库返回的数据
        //    6.2:shiroUser.getPassword() 从数据库中查询出的密码
        //    6.3:getName()  该方法会自动获得realm的名称
        //    6.4 :第6步的思路:realm类中有两个方法,一个是授权,一个是认证,我们的登录当然是在认证中了,认证之后,才能进入系统,然后                 授权。SimpleAuthenticationInfo类中,如果登录成功,即我们的sql语句是按用户的姓名得到的用户,所以姓名没有问题,然后SimpleAuthenticationInfo会根据从数据库中查询出来的密码和用户数据的密码来比较,如果正确则登录成功,否则登录失败

        ApiResult apiResult = new ApiResult();

        // 基于shiro实现登录
        Subject subject = SecurityUtils.getSubject();  
        // 用户名和密码信息
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try {
            subject.login(token);
            apiResult.setMessage("登录成功");
            apiResult.setSuccess(true);

        }catch (Exception e){
            apiResult.setMessage("登录失败");
            apiResult.setSuccess(false);
        }
        return apiResult;
    }

自定义realm

package com.jgs.shirourl.realm;

import com.jgs.shirourl.entity.ShiroUser;
import com.jgs.shirourl.service.impl.ShiroUserServiceImpl;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component("myRealm") // 这里起的名称在配置文件中要用到
public class MyRealm  extends AuthorizingRealm{


    // 自定义realm 实现 安全数据 连接

    @Autowired(required = true)
    public ShiroUserServiceImpl userService;


    // 授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("shiro 授权管理...");
        return null;
    }

    // 认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("shiro 认证管理...");

        // 转换token
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;


        // 根据用户名查询用户信息
        ShiroUser shiroUser = userService.selectByUserName(usernamePasswordToken.getUsername());

        if(shiroUser == null){
            // 用户不存在
            // 参数1:期望登录后,保存在subject中的信息
            // 参数2:如果返回null,说明用户不存在,报用户名
            // 参数3:realm名
            return null;
        }else{
            // 用户存在
            // 返回用户密码时,securityManager会自动比较返回的密码和用户输入的密码是否一致
            // 如果密码一致,则提示登录成功,如果密码不一致则报密码错误的异常
            return new SimpleAuthenticationInfo(shiroUser,shiroUser.getPassword(),getName());
        }

    }
}

然后我们还要在spring-shiro.xml文件中的securityManager中设置我们自定义的realm
spring-shiro.xml

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm"  ref="myRealm"></property>
    </bean>

spring-shiro.xml的全部代码

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


   <!--<bean id="myRealm" class="com.jgs.shirourl.realm.MyRealm"></bean>-->
    <!--安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm"  ref="myRealm"></property>
    </bean>
    <!-- 配置Shiro核心Filter  -->
    <bean id="shiroFilter"
          class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 安全管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 未认证,跳转到哪个页面  -->
        <property name="loginUrl" value="/login.html" />
        <!-- 登录页面页面 -->
        <property name="successUrl" value="/index.html" />
        <!-- 认证后,没有权限跳转页面 -->
        <property name="unauthorizedUrl" value="/unauthorized.html" />
        <!-- shiro URL控制过滤器规则  -->
        <!--这里配置过后,所有的请求都经过shiro,然后也只有一下配置了,不登录可以问的才能访问,其他(/**)的都需要登录后才能访问-->
        <property name="filterChainDefinitions">
            <value>
                /login.html* = anon   <!--*表示访问页面的同时,允许传递参数-->
                /login.js = anon
                <!--这里要释放css、和js-->
                /admin/sys/libs/** = anon    <!--两个**表示该文件夹以及该文件夹下的所有子文件夹 -->

                <!--还要释放,登录的接口-->
                /admin/main/user/login.do* = anon

              <!--  /pages/base/courier.html* = perms[courier:list]
                /pages/base/area.html* = roles[base]-->
                /** = authc    <!--登录后才能访问-->
            </value>
            <!--
            anon  未认证可以访问
            authc  认证后可以访问
            perms 需要特定权限才能访问
            roles 需要特定角色才能访问
            user 需要特定用户才能访问
            port  需要特定端口才能访问
            reset  根据指定 HTTP 请求访问才能访问
            -->
        </property>
    </bean>



    <!--shiro后处理器-->
    <bean id="lifecycleBeanPostProcessor"
          class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

</beans>

bug
推荐一个解决bug的帖子:
https://blog.csdn.net/u014206695/article/details/54672219
ssm和shiro整合,shiro的自定义的realm不能自动注入的问题

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shiroFilter' defined in
class path resource [spring-shiro/spring-shiro.xml]:
Cannot resolve reference to bean 'securityManager' 
while setting bean property 'securityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'securityManager' defined in class path resource [spring-shiro/spring-shiro.xml]:
Cannot resolve reference to bean 'myRealm' while setting bean property 'realm'; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'myRealm':
Injection of autowired dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Could not autowire field: private com.englishload.service.UserService com.englishload.realm.MyRealm.userService; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
[com.englishload.service.UserService]
found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. 
Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

注意1:因为我所有的spring配置,和spring整个的东西都放在了springmvc里了,所以这里加载的spring-mvc就是加载了spring
注意2:有一个小bug:在web.xml配置文件中如果spring-shiro.xml配置文件的加载在spring之前那么会报,在spring-shiro.xml配置文件的,权限管理securityManager中找不到我们自定义的这个realm。
原因:在项目加载的时候,容器先加载了spring-shiro.xml,但是此时的realm还没有被spring所扫描,于是就爆出了找不到这个realm类。
解决方法,将spring-shiro.xml放在spring-sprigngmvc.xml的后面。问题解决。
注:我这里的springmvc其实就是spring整合各个框架的资料,就是spring文件。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <!--<param-value>classpath:spring-mybatis.xml spring-service.xml</param-value>-->
    <!-- <param-value>classpath:spring-*.xml</param-value>-->
    <param-value> classpath:spring-mybatis.xml,
                    classpath:spring-mvc.xml,
                    classpath:spring-shiro.xml,
    </param-value>
  </context-param>

附上web.xml的全部代码

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
              http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

  <!--<display-name>shiro-web</display-name>-->
  <display-name>shiro-web</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <!--<param-value>classpath:spring-mybatis.xml spring-service.xml</param-value>-->
    <!-- <param-value>classpath:spring-*.xml</param-value>-->
    <param-value> classpath:spring-mybatis.xml,
                    classpath:spring-mvc.xml,
                    classpath:spring-shiro.xml,
    </param-value>
  </context-param>



  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param>

  <!-- 编码过滤器 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- spring监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--shiro的Filter-->
  <!--也就是说,在spring中就得有一个id为shiroFilter的bean-->
<filter>
  <filter-name>shiroFilter</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern><!--这里配置/*:即为拦截所有的资源-->
  </filter-mapping>




  <servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
  </servlet>
  <servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <!-- 此处也可以配置成 *.do 形式 -->
   <!--<url-pattern>*.do</url-pattern>-->
   <url-pattern>/admin/main/*</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>/index.jsp</welcome-file>
  </welcome-file-list>

  <!-- session配置 -->
  <session-config>
    <session-timeout>15</session-timeout>
  </session-config>

  <!-- springMVC有个拦截器会拦截js css这些引用文件,配置以下取消拦截 -->
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.gif</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>


</web-app>

猜你喜欢

转载自blog.csdn.net/java_xxxx/article/details/81672513
今日推荐