天天看点

SSM练习第五天

文章目录

  • ​​第一章:授权操作​​
  • ​​第一节:经典权限5张表的关系分析​​
  • ​​1、数据库模型关系图​​
  • ​​2、用户角色关系表​​
  • ​​3、角色权限关系表​​
  • ​​4、修改用户SysUser实体​​
  • ​​5、修改角色Role实体​​
  • ​​6、修改权限Permission实体​​
  • ​​第二节:查看用户的角色详情功能​​
  • ​​1、页面入口​​
  • ​​2、编写Controller​​
  • ​​3、编写Service​​
  • ​​4、编写Dao​​
  • ​​编写userDao​​
  • ​​编写RoleDao(user下有role)​​
  • ​​编写PermissionDao(Role下有permission)​​
  • ​​5、修改前端user-show.jsp​​
  • ​​第三节:为用户分配角色-角色列表数据回显​​
  • ​​1、页面入口​​
  • ​​2、编写Controller​​
  • ​​3、页面回显数据(user-role-add.jsp)​​
  • ​​第四节:为用户分配角色-更新关系到用户角色中间表​​
  • ​​1、页面入口​​
  • ​​2、编写Controller​​
  • ​​3、编写Service​​
  • ​​4、编写Dao​​
  • ​​第五节:为角色添加权限数据回显(和user添加role雷同)​​
  • ​​1、页面入口​​
  • ​​2、编写Controller​​
  • ​​3、编写Service​​
  • ​​4、编写Dao​​
  • ​​5、前端页面(role-permission-add.jsp)​​
  • ​​第六节:为角色添加权限-实际保存即往中间表插入记录(和user添加role雷同)​​
  • ​​1、页面入口​​
  • ​​2、编写Controller​​
  • ​​3、编写service​​
  • ​​4、编写dao​​
  • ​​第七节:为用户设置真正的角色​​
  • ​​第二章:授权后的安全控制​​
  • ​​第一节:在JSP页面控制菜单权限(不显示UI)​​
  • ​​1、spring-security.xml配置文件​​
  • ​​第二节:在服务器端控制权限(拦截请求 防止用户直接输入url访问)​​
  • ​​1、修改spring-mvc.xml配置​​
  • ​​2、在需要进行控制的Controller上添加@Secured注解​​
  • ​​3、更换默认403 forbidden界面​​
  • ​​第三章:系统日志功能​​
  • ​​第一节:AOP记录日志​​
  • ​​1、日志表sys_log和实体Log​​
  • ​​2、springmvc.xml配置文件中开启aop的自动代理​​
  • ​​3、在web.xml中配置监听request对象的监听器​​
  • ​​4、编写切面类(切面类内部有增强)​​
  • ​​5、Service代码实现​​
  • ​​6、Dao代码实现​​
  • ​​第二节 查询日志​​
  • ​​1、页面入口​​
  • ​​2、编写Controller​​
  • ​​3、编写service​​
  • ​​4、编写dao​​
  • ​​5、修改domain​​
  • ​​6、修改页面​​

第一章:授权操作

第一节:经典权限5张表的关系分析

1、数据库模型关系图

SSM练习第五天

2、用户角色关系表

多对多的关系:就是两个一对多

一对多:在一的方向添加一个集合属性即可

-- 用户角色关系表  多对多 引入中间表
CREATE TABLE sys_user_role(
    userId number,
    roleId number,
    PRIMARY KEY(userId,roleId), --联合主键:两个不能同时一样
    FOREIGN KEY (userId) REFERENCES sys_USER(id),
    FOREIGN KEY (roleId) REFERENCES sys_role(id) 
)      

3、角色权限关系表

CREATE TABLE sys_role_permission(
    permissionId number,
    roleId number,
    PRIMARY KEY(permissionId,roleId),
    FOREIGN KEY (permissionId) REFERENCES sys_permission(id),
    FOREIGN KEY (roleId) REFERENCES sys_role(id) 
)      

4、修改用户SysUser实体

@Data
public class SysUser {
    private Long id;
    private String username;
    private String email;
    private String password;
    private String phoneNum;
    private int status;

    //一个用户对应多个角色
    private List<Role> roles = new ArrayList<>();//new出来 防止空指针
}      

5、修改角色Role实体

@Data
public class Role {
    private Integer id;
    private String roleName;
    private String roleDesc;

    //一个角色被多个用户所拥有
    private List<SysUser> users = new ArrayList<>();
    //一个角色拥有多个权限
    private List<Permission> permissions = new ArrayList<>();
}      

6、修改权限Permission实体

@Data
public class Permission {
    private Integer id;
    private String permissionName;
    private String url;
    private Integer pid;//写简单的pid 不能写Permission对象 随便想想也是死循环

    //一个权限被多个角色拥有
    private List<Role> roles=new ArrayList<>();
}      

第二节:查看用户的角色详情功能

1、页面入口

SSM练习第五天
<a href="${pageContext.request.contextPath}/user/details?userId=${user.id}" class="btn bg-olive btn-xs">详情</a>      

2、编写Controller

/**
     * 查询某用户的详情功能
     *  用户有哪些角色 角色分别有哪些权限
     * @param userId
     * @return
     */
    @RequestMapping("/details")
    public ModelAndView details(Integer userId){
        LogUtils.print("用户详情:"+userId);
        //查询数据--用户详情
        SysUser user=userService.findById(userId);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user",user);
        modelAndView.setViewName("user-show");
        return modelAndView;
    }      

3、编写Service

findById(Integer userId);      
@Override
    public SysUser findById(Integer userId) {
        return userdao.findById(userId);
    }      

4、编写Dao

编写userDao
@Select("select * from sys_user where id = #{userId}")
    @Results({
            @Result(id=true,property = "id",column = "id"),//主键id 最好都加上 否则后端获取id为null
            @Result(property = "roles",column = "id", //还是本张表主键id,因为是根据本张表主键id关联到role的
                    //根据userId查询角色列表
                    // cn.ahpu.dao.RoleDao.findRolesByUserId
                    many = @Many(select = "cn.ahpu.dao.RoleDao.findRolesByUserId"))
    })
    SysUser findById(Integer userId);      
编写RoleDao(user下有role)
@Select("select r.* from sys_user_role ur,sys_role r where ur.roleid=r.id and ur.userid=#{userId}")
    @Results({
            @Result(id=true,property = "id",column = "id"),//主键id 最好加上 否则id只能用一次 后端查id就是null了
            @Result(property = "permissions",column = "id", //还是本张表主键id,因为是根据本张表主键id关联到role的
                    many = @Many(select = "cn.ahpu.dao.PermissionDao.findPermissionsByRoleId"))
    })
    List<Role> findRolesByUserId(Integer userId);      
编写PermissionDao(Role下有permission)
@Select("select p.* from sys_role_permission rp,sys_permission p where rp.roleid=#{roleId} and rp.permissionid=p.id")
    List<Permission> findPermissionsByRoleId(Integer roleId);      

5、修改前端user-show.jsp

<thead>
  <tr>
    <th>名称</th>
    <th>描述</th>
  </tr>
</thead>

<tr data-tt-id="0">
  <td colspan="2">${user.username}</td>
</tr>

<tbody>
  <c:forEach items="${user.roles}" var="role" varStatus="i">
    <tr data-tt-id="${role.id}" data-tt-parent-id="0">
      <td>${role.roleName }</td>
      <td>${role.roleDesc }</td>
    </tr>
    <%--子标签的父id要唯一对应 得用${i.count}了 }
      若dao中id@Result()里id都单独配置了
      那么id可以多次使用 将i.count全部换成 role.id也不错
    --%>
    <c:forEach items="${role.permissions}" var="permission">
      <tr data-tt-id="${role.id}-${permission.id}" data-tt-parent-id="${role.id}">
        <td>${permission.permissionName}</td>
        <td>${permission.url}</td>
      </tr>
    </c:forEach>
  </c:forEach>
</tbody>      

第三节:为用户分配角色-角色列表数据回显

1、页面入口

SSM练习第五天

2、编写Controller

@RequestMapping("/addRoleToUserUI")
    public ModelAndView addRoleToUserUI(Integer userId){
        LogUtils.print("addRoleToUserUI:"+userId);
        //所有角色
        List<Role> roles = roleService.findAll();
        //当前用户拥有的角色 直接将当前用户传过去也行 延迟加载还能起点作用
        SysUser user = userService.findById(userId);
        //方便选中已有的 换种写法
        List<Role> userOwnedRoles = user.getRoles();
        //把该用户拥有的角色id存到list放到前端
        //id变成一个list 前端方便判断该id是否在数组中 el ${fn:contains(ids,id)}
        List<Integer> ids=new ArrayList<>();
        for (Role role : userOwnedRoles) {
            ids.add(role.getId());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("roles",roles);
        modelAndView.addObject("ids",ids);
        //真正执行添加业务时需要userId 因此也需要传过去
        modelAndView.addObject("userId",user.getId());//user.getId()而不直接写userId "永远使用最后一次得到的数据"
        modelAndView.setViewName("user-role-add");
        return modelAndView;
    }      

3、页面回显数据(user-role-add.jsp)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<section class="content">
  <input type="hidden" name="userId" value="${userId}"><%--用户id隐藏域--%>
  <table id="dataList"
      class="table table-bordered table-striped table-hover dataTable">
      <thead>
        <tr>
          <th class=""style="padding-right:">
          <input id="selall" 
            type="checkbox" class="icheckbox_square-blue"></th>
          <th class="sorting_asc">ID</th>
          <th class="sorting">角色名称</th>
          <th class="sorting">角色描述</th>                  
        </tr>
      </thead>
      <tbody>

        <c:forEach items="${roles}" var="role">
          <tr>
            <td><input name="ids" type="checkbox" value="${role.id}"
              <%--<c:if test="${fn:contains(ids, role.id)}">checked="checked"</c:if>--%>
                <%--大神更牛的写法--%>
                ${fn:contains(ids, role.id)?"checked":" "}

            ></td>
            <td>${role.id}</td>
            <td>${role.roleName }</td>
            <td>${role.roleDesc}</td>
            
          </tr>
        </c:forEach>
      </tbody>

    </table>      

第四节:为用户分配角色-更新关系到用户角色中间表

1、页面入口

SSM练习第五天

2、编写Controller

/**
     * 给指定用户添加指定角色
     * @param userId 需要添加角色的用户id
     * @param ids    添加角色的id数组
     * @return
     */
    @RequestMapping("/addRolesToUser")
    public String addRolesToUser(Integer userId,Integer[] ids){
        userService.addRolesToUser(userId,ids);
        return "redirect:/user/findAll";
    }      

3、编写Service

接口

/**
     * 添加角色列表到用户
     * @param userId
     * @param ids
     */
    void addRolesToUser(Integer userId, Integer[] ids);      

实现

@Override
    public void addRolesToUser(Integer userId, Integer[] ids) {
        //先清空该用户拥有的所有角色
        userdao.delRolesFromUser(userId);

        if(ids!=null){
            for (Integer roleId : ids) {
                userdao.saveRoleToUser(userId,roleId);
            }
        }
    }      

4、编写Dao

/**
     * 清空用户原来有的所有角色
     * @param userId
     */
    @Delete("delete from sys_user_role where userId=#{userId}")
    void delRolesFromUser(Integer userId);

    /**
     * 为用户添加一个角色
     * @param userId
     * @param roleId
     */
    @Insert("insert into sys_user_role values(#{param1},#{param2})")
    void saveRoleToUser(Integer userId, Integer roleId);      

第五节:为角色添加权限数据回显(和user添加role雷同)

1、页面入口

SSM练习第五天
<a href="${pageContext.request.contextPath}/role/addPermissionsToRoleUI?roleId=#{role.id}" class="btn bg-olive btn-xs">添加权限</a>      

2、编写Controller

/**
     * 添加权限到角色的数据回显
     * @param roleId
     * @return
     */
    @RequestMapping("/addPermissionsToRoleUI")
    public ModelAndView addPermissionsToRoleUI(Integer roleId){
        LogUtils.print(roleId);

        //获取所有权限
        List<Permission> permissions = permissionService.findAll();

        //获取角色已有权限id列表
        Role role = roleService.findById(roleId);
        List<Permission> rolePermissions = role.getPermissions();
        List<Integer> ids=new ArrayList<>();
        for (Permission p : rolePermissions) {
            ids.add(p.getId());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("permissions",permissions);
        modelAndView.addObject("ids",ids);
        modelAndView.addObject("roleId",roleId);
        modelAndView.setViewName("role-permission-add");

        return modelAndView;
    }      

3、编写Service

findById(Integer roleId);      
@Override
    public Role findById(Integer roleId) {
        return roleDao.findById(roleId);
    }      

4、编写Dao

@Select("select * from sys_role where id=#{roleId}")
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "permissions",column = "id",
                    many=@Many(select = "cn.ahpu.dao.PermissionDao.findPermissionsByRoleId",fetchType = FetchType.LAZY)
            )
    })
    Role findById(Integer roleId);      

5、前端页面(role-permission-add.jsp)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<form
  action="${pageContext.request.contextPath}/role/addPermissionToRole.do"
  method="post">
  <!-- 正文区域 -->
  <section class="content"> <input type="hidden" name="roleId"
    value="${roleId}">
    <table id="dataList"
        class="table table-bordered table-striped table-hover dataTable">
        <thead>
          <tr>
            <th class=""style="padding-right:">
            <input id="selall" 
              type="checkbox" class="icheckbox_square-blue"></th>
            <th class="sorting_asc">ID</th>
            <th class="sorting">权限名称</th>
            <th class="sorting">URL</th>                  
          </tr>
        </thead>
        <tbody>
          <c:forEach items="${permissions}" var="permission">
            <tr>
              <td><input name="ids" type="checkbox" value="${permission.id}"
                     ${fn:contains(ids, permission.id)?"checked":""}
              ></td>
              <td>${permission.id}</td>
              <td>${permission.permissionName }</td>
              <td>${permission.url}</td>
              
            </tr>
          </c:forEach>
        </tbody>

      </table>
  <!--订单信息/--> <!--工具栏-->
  <div class="box-tools text-center">
    <button type="submit" class="btn bg-maroon">保存</button>
    <button type="button" class="btn bg-default"
      onclick="history.back(-1);">返回</button>
  </div>
  <!--工具栏/--> </section>
  <!-- 正文区域 /-->
</form>      

第六节:为角色添加权限-实际保存即往中间表插入记录(和user添加role雷同)

1、页面入口

SSM练习第五天
SSM练习第五天

2、编写Controller

/**
     * 为角色添加权限 动数据库表
     * @param roleId
     * @param ids
     * @return
     */
    @RequestMapping("/addPermissionsToRole")
    public String addPermissionsToRole(Integer roleId,Integer[] ids){
        LogUtils.print(ids);
        LogUtils.print(roleId);

        roleService.addPermissionsToRole(roleId,ids);
        return "redirect:/role/findAll";
    }      

3、编写service

void addPermissionsToRole(Integer roleId, Integer[] ids);      
/**
     * 为角色roleId 添加 ids这么多的权限
     * @param roleId
     * @param ids
     */
    @Override
    public void addPermissionsToRole(Integer roleId, Integer[] ids) {
        //删除roleId所有权限
        roleDao.delPermissionsFromRole(roleId);

        if(ids!=null){
            for (Integer permissionId : ids) {
                roleDao.savePermissionToRole(roleId,permissionId);
            }
        }

    }      

4、编写dao

@Delete("delete from sys_role_permission where roleid=#{roleId}")
    void delPermissionsFromRole(Integer roleId);

    /**
     * 注意参数顺序
     * @param roleId
     * @param permissionId
     */
    @Insert("insert into sys_role_permission values(#{param2},#{param1})")
    void savePermissionToRole(Integer roleId, Integer permissionId);      

第七节:为用户设置真正的角色

修改UserServiceImpl的loadUserByUsername方法,为用户设置真正的角色

/**
     * 通过用户名 得到用户对象
     * 创建用户详情对象,返回
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名获取用户(SysUser)对象--同时查询相应角色
        SysUser sysUser = userdao.findByUsername(username);
        if(sysUser==null) return null;
        /* 添加的都是假的角色对象  真正的角色对象需要到数据库内查
        //配置文件里没有指定角色了 需要自己创建角色对象
        //创建角色的集合对象
        Collection<GrantedAuthority> authorities=new ArrayList<>();
        //创建临时角色对象  正常情况下应该是数据库角色表中查的
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
        //对象添加到集合中
        authorities.add(grantedAuthority);
        //User是安全框架内实现了UserDetails接口的一个类
        //第三个参数是:角色列表对象 (此处角色名ROLE_USER自己定义 xml里需要这个名字)
        //{noop}前缀表示不加密  该把不加密去掉了*/

        //添加数据库里真正的角色对象
        Collection<GrantedAuthority> authorities=new ArrayList<>();
        //登录时授权给用户 有哪些权限就授予哪些权限
        for (Role role : sysUser.getRoles()) {
            //LogUtils.print(role.getRoleName());
            //创建角色对象
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+role.getRoleName());
            //添加角色对象到集合
            authorities.add(grantedAuthority);
        }

        //用户名 密码 角色
        UserDetails user = new User(sysUser.getUsername(),sysUser.getPassword(),authorities);
        return user;
    }      

userdao.findByUsername(username);,加个一对多结果集映射,否则查不到用户所拥有的权限

//根据用户名查询用户对象 唯一对象(username必须唯一)
    @Select("select * from sys_user where username = #{username} and status=1")
    @Results({
            @Result(id=true,property = "id",column = "id"),//主键id 最好都加上 否则后端获取id为null
            @Result(property = "roles",column = "id", //还是本张表主键id,因为是根据本张表主键id关联到role的
                    many = @Many(select = "cn.ahpu.dao.RoleDao.findRolesByUserId",fetchType = FetchType.LAZY))
    })
    SysUser findByUsername(String username);      

第二章:授权后的安全控制

第一节:在JSP页面控制菜单权限(不显示UI)

在JSP页面中使用security:authorize标签,可以控制菜单是否显示。security:authorize标签的 access=“hasAnyRole(‘ROLE_USER’,‘ROLE_ADMIN’)”。因为标签的access使用的是表达式,所以需要将spring- security.xml配置文件的use-expressions设置为true。

1、spring-security.xml配置文件

1、使用表达式改为true: use-expressions=“true”

2、access不能直接写权限名而要写成:access="hasAnyRole(‘ROLE_USER’,‘ROLE_ADMIN’)”

表示拥有’ROLE_USER’,'ROLE_ADMIN’中任意一个权限就初步可以访问/**任意路径(真正目的是啥权限都没有时无法访问本网站任意未放行路径)

3、前端jsp 先加上标签库 然后

系统管理UI用 <security:authorize access=“hasRole(‘ROLE_ADMIN’)”></security:authorize>包裹

基础数据UI用<security:authorize access=“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_USER’)”></security:authorize>包裹

SSM练习第五天

jsp:

<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<security:authorize access="hasRole('ROLE_ADMIN')">
  <li class="treeview"><a href="#"> <i class="fa fa-cogs"></i>
      <span>系统管理</span> <span class="pull-right-container"> <i
        class="fa fa-angle-left pull-right"></i>
    </span>
  </a>
    <ul class="treeview-menu">
  
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/user/findAll"> <i
          class="fa fa-circle-o"></i> 用户管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/role/findAll"> <i
          class="fa fa-circle-o"></i> 角色管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/permission/findAll">
          <i class="fa fa-circle-o"></i> 权限管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/pages/syslog-list.jsp"> <i
          class="fa fa-circle-o"></i> 访问日志
      </a></li>
    </ul></li>
</security:authorize>

<%--ROLE_ADMIN和ROLE_USER都可以使用 换言之啥权限都没有连基础数据都不能访问--%>
<security:authorize access="hasAnyRole('ROLE_ADMIN','ROLE_USER')">
  <li class="treeview"><a href="#"> <i class="fa fa-cube"></i>
      <span>基础数据</span> <span class="pull-right-container"> <i
        class="fa fa-angle-left pull-right"></i>
    </span>
  </a>
    <ul class="treeview-menu">
  
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/product/findAll">
          <i class="fa fa-circle-o"></i> 产品管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/order/findAll">
          <i class="fa fa-circle-o"></i> 订单管理
      </a></li>
    </ul></li>
</security:authorize>      

第二节:在服务器端控制权限(拦截请求 防止用户直接输入url访问)

虽然页面菜单对不同角色显示类不同的菜单,但是如果直接访问服务器端的url还是可以访问到资源信息的,所以好 需要对服务器端的资源进行安全控制。

控制方式就是借助于Spring的AOP,对Controller的的访问进行权限的功能增强。 修改spring-security.xml配置文件,添加aop的自动代理

1、修改spring-mvc.xml配置

添加配置:

<!--AOP 当下面一行的配置也放在spring-mvc.xml本文件内时此配置可有可无
        当下面一行配置放在spring-security.xml中 本配置必须有 开启注解动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<!--配置开启security的注解支持 必须加上-->
<security:global-method-security secured-annotations="enabled"/>      

没办法提示了,自己手动加: 复制后改下名字即可

SSM练习第五天

2、在需要进行控制的Controller上添加@Secured注解

指定拥有哪些权限时方可访问本Controller,用户任意一个权限即可

@Secured({"ROLE_ADMIN"})
public class UserController
@Secured({"ROLE_ADMIN"})
public class RoleController
@Secured({"ROLE_ADMIN"})
public class PermissionController

@Secured({"ROLE_ADMIN","ROLE_USER"})
public class ProductController      

3、更换默认403 forbidden界面

spring-security.xml里加一行配置

<security:access-denied-handler error-page="/failer.jsp"></security:access-denied-handler>      
SSM练习第五天

第三章:系统日志功能

第一节:AOP记录日志

1、日志表sys_log和实体Log

sql语句

create sequence log_seq;

CREATE TABLE sys_log(
    id number PRIMARY KEY,
    visitTime DATE,
    username VARCHAR2(50),
    ip VARCHAR2(30),
    method VARCHAR2(200)
)      

实体类

SysLog.java

@Data
public class SysLog {
    private Long id;
    private Date visitTime;//访问时间
    private String username;//访问者用户名
    private String ip;//访问者ip
    private String method;//访问的全限定类名.方法
}      

2、springmvc.xml配置文件中开启aop的自动代理

之前应该已经加过了

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>      

3、在web.xml中配置监听request对象的监听器

springmvc会自动将httpservletrequest放到IOC容器中 即springmvc不需要加此配置

其他框架不会 其他web层框架想要注入request就必须加此配置

<!--配置请求监听器:当请求发生时,在容器中创建请求对象-->
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>      

4、编写切面类(切面类内部有增强)

SSM练习第五天

spring-mvc.xml得多扫描一个包了

SSM练习第五天

LogAop代码实现

package cn.ahpu.log;

import cn.ahpu.domain.SysLog;
import cn.ahpu.domain.SysUser;
import cn.ahpu.service.LogService;
import cn.ahpu.utils.LogUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @author 寒面银枪
 * @create 2020-05-16 16:53
 *
 * 注解配置AOP
 * 提供一个类配置为切面类
 *  切面=切入点+通知
 *      通知类型
 *          前置增强:@Before
 *          后置增强:@AfterReturning
 *          最终增强:@After
 *          异常增强:@AfterThrowing
 *          环绕增强:@Around
 *
 */
//注意xml里加上扫描log包
@Component
@Aspect
public class LogController {

    @Autowired
    LogService logService;

    @Autowired
    HttpServletRequest request;//只有springmvc放到IOC中了 换一个框架可能就没了

    @Pointcut("execution(* cn.ahpu.controller.*.*(..))")
    public void pointcut(){}

    /**
     * 环绕增强
     * @param joinPoint 连接点对象--可以执行真实方法--只在环绕增强中使用
     *        连接点就是拦截的方法
     */
    @Around("pointcut()") //织入
    public Object around(ProceedingJoinPoint joinPoint){
        //创建日志对象
        SysLog sysLog = new SysLog();

        //将日志对象封装
        //1.访问时间 visitTime
        sysLog.setVisitTime(new Date());

        //2.访问用户名 username 安全框架内得到 注意是安全框架的User 不是自己写SysUser
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        sysLog.setUsername(user.getUsername());

        //3.访问ip 还是得得到request对象 仔细想想哪里有? IOC容器里肯定有 直接注入一下就行了嘛
        String ipAddr = request.getRemoteAddr();
        sysLog.setIp(ipAddr);

        //4.访问全限定类名
        //被拦截类的全限定类名
        String className = joinPoint.getTarget().getClass().getName();
        //方法名称
        String methodName = joinPoint.getSignature().getName();
        sysLog.setMethod(className+"."+methodName);

        //将日志对象存储到数据库
        LogUtils.print(sysLog);
        logService.save(sysLog);


        try {
            //执行真实的方法--必须返回真实方法返回值 否则所有方法被拦截 返回值都不返回 就不会跳转了
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}      

5、Service代码实现

LogService

public interface LogService {
    void save(SysLog sysLog);
}      
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    LogDao logDao;

    @Override
    public void save(SysLog log) {
        logDao.save(log);
    }
}      

6、Dao代码实现

public interface LogDao {
    @Insert("insert into sys_log values(log_seq.nextval,#{visitTime},#{username},#{ip},#{method})")
    void save(SysLog log);
}      

第二节 查询日志

1、页面入口

SSM练习第五天
SSM练习第五天

2、编写Controller

/**
     * 查询所有日志  分页查询
     */
    @RequestMapping("/findAll")
    public ModelAndView findAll(
            @RequestParam(value = "currPage",required = false,defaultValue = "1") Integer currPage,
            @RequestParam(value = "pageSize",required = false,defaultValue = "5") Integer pageSize
    ){
        PageInfo<SysLog> pageHelper = logService.findAllByPageHelper(currPage, pageSize);

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("pageHelper", pageHelper);
        modelAndView.setViewName("syslog-list");

        return modelAndView;
    }      

3、编写service

public interface LogService {

    void save(SysLog sysLog);

    /**
     * 查询所有日志
     * @return
     */
    List<SysLog> findAll();

    /**
     * 分页查询所有
     * @param currPage
     * @param pageSize
     * @return
     */
    PageInfo<SysLog> findAllByPageHelper(Integer currPage, Integer pageSize);
}      
@Override
    public PageInfo<SysLog> findAllByPageHelper(Integer currPage, Integer pageSize) {
        //指定分页参数
        PageHelper.startPage(currPage,pageSize);
        //查询全部 分页参数已经被绑定到当前线程 拦截器根据参数帮你做到拦截
        List<SysLog> logs = logDao.findAll();
        //创建PageInfo对象 控制页面最多显示5个页码
        PageInfo<SysLog> pageInfo = new PageInfo<>(logs, 5);
        //返回
        return pageInfo;
    }      

4、编写dao

@Select("select * from sys_log")
    List<SysLog> findAll();      

5、修改domain

@Data
public class SysLog {
    private Long id;
    private Date visitTime;//访问时间
    private String visitTimeStr;
    private String username;//访问者用户名
    private String ip;//访问者ip
    private String method;//访问的全限定类名.方法

    public String getVisitTimeStr() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String timeStr = sdf.format(visitTime);
        return timeStr;
    }
}      

6、修改页面

<table id="dataList"
      class="table table-bordered table-striped table-hover dataTable">
      <thead>
        <tr>
          <th class=""style="padding-right:"><input id="selall"
            type="checkbox" class="icheckbox_square-blue"></th>
          <th class="sorting_asc">ID</th>
          <th class="sorting">访问时间</th>
          <th class="sorting">访问用户</th>
          <th class="sorting">访问IP</th>
          <th class="sorting">访问方法</th>
        </tr>
      </thead>
      <tbody>
        <c:forEach items="${pageHelper.list}" var="syslog" varStatus="i">
          <tr>
            <td><input name="ids" type="checkbox"></td>
            <td>${i.count}</td>
            <td>${syslog.visitTimeStr }</td>
            <td>${syslog.username }</td>
            <td>${syslog.ip }</td>
            <td>${syslog.method}</td>
          </tr>
        </c:forEach>
      </tbody>

    </table>
    <!--数据列表/-->

    <!--工具栏-->
    <div class="pull-left">
      <div class="form-group form-inline">
        <div class="btn-group">
          <button type="button" class="btn btn-default" title="刷新"
            onclick="window.location.reload();">
            <i class="fa fa-refresh"></i> 刷新
          </button>
        </div>
      </div>
    </div>
    <div class="box-tools pull-right">
      <div class="has-feedback">
        <input type="text" class="form-control input-sm"
          placeholder="搜索"> <span
          class="glyphicon glyphicon-search form-control-feedback"></span>
      </div>
    </div>
    <!--工具栏/-->
  </div>
  <!-- 数据表格 /-->

</div>
<!-- /.box-body -->

<!-- .box-footer-->
<div class="box-footer">
  <div class="pull-left">
    <div class="form-group form-inline">
      第${pageHelper.pageNum} 页,
      总共${pageHelper.pages} 页,共${pageHelper.total} 条数据。 每页
      <select class="form-control" name="pageSize" id="pageSize" onchange="gotoPage(1)">
        <%--前10个用循环写 方便--%>
        <c:forEach begin="1" end="10" var="i">
          <option value="${i}">${i}</option>
        </c:forEach>
        <option value="15">15</option>
        <option value="20">20</option>
        <option value="30">30</option>
        <option value="40">40</option>
        <option value="50">50</option>

      </select> 条
    </div>
  </div>

  <div class="box-tools pull-right">
    <ul class="pagination" id="gotoLi">
      <%--超链接里访问js函数 必须加前缀javascript:--%>
      <li><a href="javascript:gotoPage(1)" aria-label="Previous">首页</a></li>
      <%--prePage多方便--%>
      <li><a href="javascript:gotoPage(${pageHelper.prePage})">上一页</a></li>
      <%--页面上显示几个页码也简单可控了 实在太方便了--%>
      <c:forEach begin="${pageHelper.navigateFirstPage}" end="${pageHelper.navigateLastPage}" var="i">
        <li><a href="javascript:gotoPage(${i})">${i}</a></li>
      </c:forEach>
      <li><a href="javascript:gotoPage(${pageHelper.nextPage})">下一页</a></li>
      <li><a href="javascript:gotoPage(${pageHelper.pages})" aria-label="Next">尾页</a></li>
    </ul>
  </div>

</div>      
<script type="text/javascript">
        //方法写在外面 会自动执行 注意先得给每个option加上value="xx" 不能没有value属性
        //每页显示几条的数据回显 select option
        $("#pageSize option[value=${pageHelper.pageSize}]").prop("selected","selected");

        //跳转页面
        function gotoPage(currPage) {
            //页面的越界检查此处判断也很方便
            if(currPage<1||currPage>${pageHelper.pages}) return;

            var pageSize=$("#pageSize").val();
            location.href="${pageContext.request.contextPath}/log/findAll?currPage="+currPage+"&pageSize="+pageSize;
        }

  </script>