Apache Shiro(二)shiro+ssm项目整合


前提:创建一个mavenWeb项目,导入相关依赖
项目结构:
在这里插入图片描述
在这里插入图片描述
项目访问流程:
在这里插入图片描述

一、导入相关依赖

在SSM项目依赖的基础上导入以下依赖:

  	<properties>   
    	<shiro.version>1.4.1</shiro.version>
 	</properties>
 	<dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>${shiro.version}</version>
    </dependency>

二、配置ssm的文件

(1)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"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--包扫描-->
    <context:component-scan base-package="com.fy.service"/>
    <!--配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/ssm_shiro"/>
    </bean>

    <!--SqlSessionFactoryBean-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!--为Dao生成实现类 mybatis-plus-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.fy.dao"/>
    </bean>

    <!--事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--开启事务的注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

<!--    配置shiro-->
<!--    我们普通的java工程中需要手动创建相关的对象,在spirng项目中就将相关对象的创建交给spirng核心容器-->
<!--  创建安全管理器  -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--        将realm交给安全管理器管理-->
        <property name="realm" ref="myrealm"></property>
    </bean>
<!--    创建realm-->
    <bean id="myrealm" class="com.fy.realm.MyRealm">
        <property name="credentialsMatcher" ref="hashedCredentialsMatcher"></property>
    </bean>
<!--    创建密码匹配器-->
    <bean id="hashedCredentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="MD5"></property>
        <property name="hashIterations" value="1024"></property>
    </bean>
<!--    设置shiro的过滤规则,该id值有一定的意义,要在web.xml中使用-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"></property>

<!--        如果没有登录,跳转到登录页面-->
        <property name="loginUrl" value="/tologin"></property>

<!--        如果没有授权时访问,跳转的页面-->
        <property name="unauthorizedUrl" value="/unauthorized.jsp"></property>
<!--       其他路径的过滤 anon表示不用认证即可访问,authc表示需要认证才能访问-->
        <property name="filterChainDefinitions">
            <value>
                /index.jsp=anon
                /login=anon
                /static/**=anon
                /**=authc
            </value>
        </property>
    </bean>
</beans>

(2)Springmvc配置文件

<?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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--包扫描-->
    <context:component-scan base-package="com.fy.controller"/>

    <!--静态资源放行-->
    <mvc:default-servlet-handler/>
    <!--注解驱动-->
    <mvc:annotation-driven/>

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 启动Shiro的注解 -->
    <!-- 要在项目中使用shiro的注解,所以要启动shiro注解 -->
    <bean id="lifecycleBeanPostProcessor"
          class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    <bean
            class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
            depends-on="lifecycleBeanPostProcessor" />
    <bean
            class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <!-- ref中的值是spring配置文件中安全管理器的id名-->
        <property name="securityManager" ref="securityManager" />
    </bean>
</beans>

(3)web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  <!--配置shiro的过滤器-->
  <filter>
    <!--必须和shiro的过滤规则的id一致-->
<!--    该过滤器的作用就是把filter和spring中的bean关联起来-->
    <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>


  <!--监听器:加载spring配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--前端控制器-->
  <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:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

三、创建jsp页面

Login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登陆页码</h1>
<form action="/login" method="post">
    账号:<input type="text" name="username"/><br>
    密码:<input type="text" name="userpwd"/><br>
    <input type="submit" value="登陆"/>
    <input type="button" value="注册" onclick="location.href='regist'"/>
</form>
</body>
</html>

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入shiro的标签库--%>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登录成功,欢迎来到龙泉镇</h1>
<%--   
<shiro:hasPermission name="user:query"> 
会访问Realm的权限分配方法,检查目前用户是否拥有相关的权限
--%>
<shiro:hasPermission name="user:query">
    <a href="/user/query">查询所有用户</a>
</shiro:hasPermission>

<shiro:hasPermission name="user:update">
    <a href="/user/update">修改用户</a><br>
</shiro:hasPermission>

<shiro:hasPermission name="user:delete">
    <a href="/user/delete">删除用户</a><br>
</shiro:hasPermission>

<shiro:hasPermission name="user:insert">
    <a href="/user/insert">添加用户</a><br>
</shiro:hasPermission>

<shiro:hasPermission name="user:export">
    <a href="/user/export">导出用户</a><br>
</shiro:hasPermission>
</body>
</html>

index.jsp

<html>
<body>
<!--访问index.jsp,自动访问"/tologin"路径-->
<jsp:forward page="/tologin"/>
</body>
</html>

unauthorized.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>该用户没有权限</h1>
</body>
</html>

四、各层的代码

(1)Controller层
①PageController

@Controller
public class PageController {
    @GetMapping("/tologin")
    public String loginPage(){
        return "login";
    }
}

②LoginController

@Controller
public class LoginController {
    @PostMapping("/login")
    public String login(String username,String userpwd){
//        使用shiro的SecurityUtils创建subject对象
        Subject subject = SecurityUtils.getSubject();
//        创建token
        UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);
        try {
//            执行登录
            subject.login(token);
//            认证成功,访问success.jsp
            return "success";
        }catch (Exception e){
            e.printStackTrace();
//            认证失败,访问login.jsp
            return "login";
        }
    }
}

③UserController

@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("query")
   /* @RequiresPermissions()该注解会判断访问的用户是否拥有相应的权限,防止有人没有权限,直接从地址栏
   访问后台。使用该注解,要先在springmvc配置中启动shiro注解
   */
    @RequiresPermissions("user:query")
    public String query(){
        return "user:query";
    }

    @GetMapping("delete")
    @RequiresPermissions("user:delete")
    public String delete(){
        return "user:delete";
    }

    @GetMapping("update")
    @RequiresPermissions("user:update")
    public String update(){
        return "user:update";
    }

    @GetMapping("insert")
    @RequiresPermissions("user:insert")
    public String insert(){
        return "user:insert";
    }

    @GetMapping("export")
    @RequiresPermissions("user:export") //@Transcational
    public String export(){
        return "user:export";
    }
}

④MyHandlerController
这个是用来处理项目中的异常,如果报相应的异常,就会执行相应的逻辑,注意类上使用的注解

@ControllerAdvice
public class MyHandlerException {

    @ExceptionHandler(value = UnauthorizedException.class)
    public void unauthorizedException(){
        System.out.println("quanxianbugou");
    }
}

(2)entity 实体层

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer userid;
    private String username;
    private String userpwd;
    private String sex;
    private String address;
    private String salt; //盐
}

(3) dao层
UserDao:

public interface UserDao {

    /**
     * 根据用户名查询用户信息
     * @return
     */
    public User selectByUsername(@Param("username") String username);
}

PermissionDao:`

public interface PermissionDao {
//    根据用户id查询权限
    List<String> findAllPermissionByUserId(Integer userid);
}

(4)service层
UserService接口:

public interface UserService {
    public User selectByUsername(String username);
}

PermissionService接口:

public interface PermissionService {
    List<String> findAllPermissionByUserId(Integer userid);
}

接口实现类:
UserServiceImpl:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    public User selectByUsername(String username) {
        User user = userDao.selectByUsername(username);
        return user;
    }
}

PermissionServiceImpl:

@Service
public class PermissionServiceImpl implements PermissionService {
    @Autowired
    private PermissionDao permissionDao;


    public List<String> findAllPermissionByUserId(Integer userid) {
        List<String> permission = permissionDao.findAllPermissionByUserId(userid);
        return permission;
    }
}

(5)映射文件
UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fy.dao.UserDao">

    <select id="selectByUsername" resultType="com.fy.entity.User">
        select * from user where username=#{username}
    </select>
</mapper>

PermissionMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fy.dao.PermissionDao">


    <select id="findAllPermissionByUserId" resultType="java.lang.String">
        select percode from user_role ur,role_permission rp, permission p
        where ur.roleid=rp.roleid and rp.perid=p.perid and ur.userid=#{userid}
    </select>
</mapper>

(6)realm
MyRealm:

public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserService userServiceimpl;

    @Autowired
    private PermissionService permissionServiceimpl;

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//       获取认证方法中获取到用户信息
        User user =(User) principalCollection.getPrimaryPrincipal();
//        查询用户对应的权限
        List<String> permissionByUserId = permissionServiceimpl.findAllPermissionByUserId(user.getUserid());
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if(permissionByUserId!=null){
//            将权限绑定到SimpleAuthorizationInfo对象
            info.addStringPermissions(permissionByUserId);
        }
        return info;
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//       获取token传递过来的username
        String username = authenticationToken.getPrincipal().toString();
//        根据用户名查询用户信息
        User user = userServiceimpl.selectByUsername(username);
//        如果查询的结果不为空
        if(user!=null){
//            将查询到的信息中的盐值进行相应的处理
            ByteSource salt = ByteSource.Util.bytes(user.getSalt());
//            该对象中传的user会被上边的授权方法获取到,以及subject对象也可以获取到
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getUserpwd(), salt, this.getName());
            return authenticationInfo;
        }
        return null;
    }
}

五、前后端完全分离的写法

前后端完全分离的模式:对于前端的请求,后台不再跳转页面,而是返回给请求者一个json数据。

该项目中有三个地方需要改造返回json数据:
1、认证成功或认证失败时
2、出现异常时,例如权限不足时
3、未登录访问时

操作:
(1)创建返回的json数据格式

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult {
    private Integer code;//编码
    private String msg;//信息
    private Object data;//要传递的数据
}

(2)改造Controller

	将Controller层的@Controller都改成@RestController,将@ControllerAdvice改成@RestControllerAdvice

①LoginController

@PostMapping("login")
    public CommonResult login(String username, String userpwd){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(username,userpwd);
        try {
            subject.login(token);//realm的认证功能
//getPrincipl()可以得到SimpleAuthenticationInfo中的数据
            Object user = subject.getPrincipal();
            return new CommonResult(2000,"登陆成功",user);
        }catch (Exception e){
            return new CommonResult(5000,"登陆失败",null);
        }
    }

②MyHandlerController

@RestControllerAdvice
public class MyHandlerException {

    @ExceptionHandler(value = UnauthorizedException.class)
    public CommonResult unauthorizedException(){
        return new CommonResult(5001,"权限不足",null);
    }
}

③未登录访问时:有两种改造方法
第一种:更改PageController

@RestController
public class PageController {
    @GetMapping("/tologin")
    public CommonResult loginPage(){
        return new CommonResult(5002,"请先登录",null);
    }
}

第二种:更改spirng配置文件,实现一个自己的过滤器
Ⅰ、修改spring配置文件

 <!--设置shiro的过滤规则
           该id值有一定的意义。
    -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
          <property name="securityManager" ref="securityManager"/>
          <!--如果没有认证 则跳转到登陆界面
          <property name="loginUrl" value="/tologin"/>
          -->
          <!--如果没有授权则跳转的页面-->
          <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <!--anon:表示允许匿名访问
            authc:需要认证后才可以访问
        -->
          <property name="filterChainDefinitions">
              <value>
                  /index.jsp=anon
                  /login=anon
                  /static/**=anon
                  /**=authc
              </value>
          </property>

        <!--配置自己的过滤器-->
        <property name="filters">
             <map>
                  <entry key="authc" value-ref="myFilter"/>
             </map>
        </property>
    </bean>

    <bean id="myFilter" class="com.fy.filter.LoginFilter"></bean>

Ⅱ、创建Filter类

public class LoginFilter extends FormAuthenticationFilter {
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
      response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        CommonResult commonResult=new CommonResult(5002,"未登录",null);
        writer.print(JSON.toJSONString(commonResult));
        return false;
    }
}

因为用到了转化json数据,所以还要导入依赖:

	<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值