zoukankan      html  css  js  c++  java
  • SpringSecurity实现权限管理和页面导航栏动态实现

    1     用户模块. 3

    1.1  需求:获取用户名. 3

    1.1.1     分析. 3

    1.1.2     服务端获取用户信息. 4

    1.1.3     页面获取用户信息. 5

    1.2  给用户分配角色. 7

    1.2.1     需求分析. 7

    1.2.2     效果预览. 7

    1.2.3     进入用户角色列表页面. 9

    1.2.4     保存. 12

    2     SpringSecurity 授权功能. 13

    2.1  在JSP页面控制菜单权限. 13

    2.1.1     基本语法. 13

    2.1.2     应用. 14

    2.2  服务端控制权限. 18

    2.2.1     为什么需要在服务端控制权限呢?. 18

    2.2.2     方式1:JSR-250注解方式权限校验. 19

    2.2.3     方式2:SpringSecurity注解方式实现权限校验. 20

    2.2.4     方式3:SpEL表达式方式实现权限校验. 20

    3     系统日志功能. 21

    3.1  需求. 21

    3.2  建表. 21

    3.3  domain 21

    3.4  dao 22

    3.5  service 22

    3.5.1     接口. 22

    3.5.2     实现. 22

    3.6  编写切面类. 23

    3.6.1     先再web.xml配置监听器. 23

    3.6.2     切面类. 23

    3.6.3     注解扫描切面类. 25

    3.6.4     访问controller测试. 25

    1   用户模块

    1.1   需求:获取用户名

    1.1.1     分析

    (1)       SecurityContext 上下文对象

    (2)       查看实现类SecurityContextImpl

     

    (3)       查看Authentication认证对象

    Authencication 封装的就是用户信息。

     

    (4)       Authentication 实现类: UsernamePasswordAuthenticationToken

    此类封装的就是用户密码的token信息

     

    (5)       这里的principal就是指SysUser对象

    (6)       Authentication 会封装到SecurityContext中

    (7)       然后,把SecurityContext绑定到当前线程上。

    (8)       最后SecurityContext会存入HttpSession中

    (9)       最终:session---àSecurityContext-----àAuthencication-------àSysUser

    1.1.2     服务端获取用户信息

    /**
     *
    查询全部
     */
    @RequestMapping("/findAll")
    public ModelAndView findAll(
            @RequestParam(required = true,defaultValue = "1") int pageNum,
            @RequestParam(required = true,defaultValue = "2") int pageSize){

        // 测试代码
        //1.
    获取绑定到当然线程上的SecurityContext
       
    SecurityContext securityContext = SecurityContextHolder.getContext();
        //2. 获取认证器对象
       
    Authentication authentication = securityContext.getAuthentication();
        //3. 获取认证身份信息. 注意这里的user
    // org.springframework.security.core.userdetails.User
        
     User user = (User) authentication.getPrincipal();

        //4. 获取用户名
       
    System.out.println(sysUser.getUsername());


        // 调用service查询
       
    List<SysUser> userList = userService.findAll(pageNum,pageSize);
        // 封装分页bean
       
    PageInfo<SysUser> pageInfo = new PageInfo<>(userList);

        ModelAndView mv = new ModelAndView();
        mv.setViewName("user-list");
        mv.addObject("pageInfo",pageInfo);
        return mv;
    }

    1.1.3     页面获取用户信息

    1.1.3.1         如何获取?

    (1)       SecurityContext对象会存入到HttpSession对象中,怎么证明这一点呢?

    我们只需要在任意一个jsp页面写上${sessionScope}就可以看到session里面的全部内容。

    {SPRING_SECURITY_CONTEXT=

    org.springframework.security.core.context.SecurityContextImpl@443cd45f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@443cd45f: Principal: org.springframework.security.core.userdetails.User@231bff: Username: Jack; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 02D659915CA438267B59625611AB657A; Granted Authorities: ROLE_USER}

     

    1.1.3.2         方式1:EL

    在jsp页面获取用户名,使用EL表达式:

    ${sessionScope.

    SPRING_SECURITY_CONTEXT.authentication.principal.username}

     

    1.1.3.3         方式2:security标签

    使用SpringSecurity标签获取用户名:

    (1)       先引入标签库描述文件

     

    (2)       使用标签

    <security:authentication property="principal.username" />

       

    应用:

     

    1.2   给用户分配角色

    1.2.1     需求分析

     

    -- 1.2  给用户分配角色 

    -- 需求:给userId=1就是jack分配:普通用户、管理员角色

     

    -- 实现:

    -- 1.先根据用户id,删除用户角色表中数据

    DELETE FROM sys_user_role WHERE userId=1;

    -- 2.再给用户分配角色

    INSERT INTO sys_user_role(userId,roleId)VALUES(1,1);

    INSERT INTO sys_user_role(userId,roleId)VALUES(1,2);

    1.2.2     效果预览

    (1)       访问用户列表

     

       

    点击添加角色:

    A.  提交到后台controller

    B.  后台

    a)   根据用户id查询SysUser用户

    b)   获取用户已经具有的角色

    String roleStr = “USER,ADMIN,”;

    c)   查询所有角色

    d)   保存

    (2)       查看用户角色列表,如果用户已经有响应角色,就默认选中

    页面判断:roleStr是否有包含(USER/ADMIN), 如果有包含,就选中

    (3)      最后点击保存,给用户添加角色

    1.2.3     进入用户角色列表页面

    1.2.3.1         修改user-list.jsp提交地址

     

     

    1.2.3.2         controller

    l  实现思路

    (1)       需要查询用户

    (2)       查询用户角色

    (3)       查询所有角色

    (4)       保存以上信息,页面回显

    (5)       注意:打断点调试sysUser中的用户id是否有数据.

    /**
     *
    用户角色
     * (1)
    进入用户角色user-role-add.jsp页面
     */
    @RequestMapping("/toUserRole")
    public ModelAndView toUserRole(Long id){
        // a.查询用户
       
    SysUser sysUser = userService.findById(id);

        // b.用户具有的角色
       
    List<Role> roles = sysUser.getRoles();
        StringBuffer sb = new StringBuffer();
        String roleStr="";
        if (roles != null && roles.size()>0){
            for(Role r : roles){
                sb.append(r.getRoleName()+",");
            }// 去除最后逗号
           
    roleStr = sb.substring(0,sb.length()-1);
        }

        // c.查询所有角色
       
    List<Role> roleList = roleService.findAll();

        // 返回结果封装
       
    ModelAndView mv = new ModelAndView();
        mv.addObject("user",sysUser);
        mv.addObject("roleStr",roleStr);
        mv.addObject("roleList",roleList);
        mv.setViewName("user-role-add");
        return mv;
    }

    (6)       写完上面代码,调试发现根据用户的id查询的SysUser对象中主键值为空,因为查询结果与延迟加载查询结果都有相同的id值,导致影响结果的封装。

    解决如下:

        

    1.2.3.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="${user.id}">
       <table id="dataList"
            
    class="table table-bordered table-striped table-hover dataTable">
             <thead>
                <tr>
                   <th class="" style="padding-right: 0px">
                   <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="${roleList}" var="role">
                   <tr>

                      <td><input
                           
    name="ids"  判断roleStr中是否有包含role.roleName
                           
    ${fn:contains(roleStr,role.roleName )? 'checked':''}
                           
    type="checkbox"
                           
    value="${role.id}"></td>
                      <td>${role.id}</td>
                      <td>${role.roleName }</td>
                      <td>${role.roleDesc}</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>
    <!-- 正文区域 /-->

    1.2.4     保存

     

    1.2.4.1         controller

    /**
     *
    用户角色
     * (2)
    给用户添加角色
     */
    @RequestMapping("/addRoleToUser")
    public String addRoleToUser(Long userId,Long[] ids){
        userService.addRoleToUser(userId,ids);
        return "redirect:/user/findAll";
    }

    1.2.4.2         service

    n  先解除关系,删除中间表数据

    n  再往中间表添加数据,即给用户添加角色关系维护

    @Override
    public void addRoleToUser(Long userId, Long[] roleIds) {
        //1. 先删除中间表当前用户相关的角色数据
        //DELETE FROM sys_user_role WHERE userId=1
       
    userDao.deleteUserRole(userId);

        //2. 添加用户角色关系
       
    if (roleIds != null &&roleIds.length>0){
            for(Long roleId : roleIds){
                userDao.addUserRole(userId,roleId);
            }
        }
    }

    1.2.4.3         dao

    /**
     *
    根据用户删除用户角色关联表数据
     * @param
    userId 用户主键
     */
    @Select("delete from sys_user_role where userId=#{userId}")
    void deleteUserRole(Long userId);

    /**
     *
    添加用户角色关系
     * @param
    userId 用户主键
     * @param
    roleId 角色主键
     */
    @Insert("insert into sys_user_role(userId,roleId)values(#{userId},#{roleId})")
    void addUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId);

    2   SpringSecurity 授权功能

    2.1   在JSP页面控制菜单权限

    2.1.1     基本语法

    (1)       页面引入标签库文件

    <%@taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

    (2)       使用标签 

    <security:authorize access="hasAnyRole('ROLE_ADMIN')">
    标签访问资源需要ROLE_ADMIN角色权限。
     

    (3)       注意

    因为这里使用的是SPEL表达式,所以需要开启表达式语言支持。

     

    2.1.2     应用

    2.1.2.1         期望结果

    (1)       期望结果:不同的用户登陆只显示用户有权限的功能菜单

    (2)       例如:Jack是普通用户登陆,

     

    看到的就只有基础数据模块

    (3)       例如:Rose是管理员登陆,

     

    看到的就是系统管理模块.

    2.1.2.2         页面实现

    <%@ page language="java" contentType="text/html; charset=UTF-8"
       pageEncoding="UTF-8"%>
    <%@taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

    <aside class="main-sidebar">
       <!-- sidebar: style can be found in sidebar.less -->
      
    <section class="sidebar">
          <!-- Sidebar user panel -->
         
    <div class="user-panel">
             <div class="pull-left image">
                <img src="${pageContext.request.contextPath}/img/user2-160x160.jpg"
                  
    class="img-circle" alt="User Image">
             </div>
             <div class="pull-left info">
                <p>
                   <security:authentication property="principal.username" />
                </p>
                <a href="#"><i class="fa fa-circle text-success"></i> 在线</a>
             </div>
          </div>

          <!-- sidebar menu: : style can be found in sidebar.less -->
         
    <ul class="sidebar-menu">
             <li class="header">菜单</li>
             <li id="admin-index"><a
               
    href="${pageContext.request.contextPath}/pages/main.jsp"><i
                  
    class="fa fa-dashboard"></i> <span>首页</span></a></li>

             <li class="treeview">
                <security:authorize access="hasAnyRole('ROLE_ADMIN')">
                   <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>
                </security:authorize>
                <ul class="treeview-menu">
                   <security:authorize access="hasAnyRole('ROLE_ADMIN')">
                   <li id="system-setting"><a
                     
    href="${pageContext.request.contextPath}/user/findAll"> <i
                        
    class="fa fa-circle-o"></i> 用户管理
                   </a></li>
                   </security:authorize>
                   <security:authorize access="hasAnyRole('ROLE_ADMIN')">
                   <li id="system-setting"><a
                     
    href="${pageContext.request.contextPath}/role/findAll"> <i
                        
    class="fa fa-circle-o"></i> 角色管理
                   </a></li>
                   </security:authorize>
                   <security:authorize access="hasAnyRole('ROLE_ADMIN')">
                   <li id="system-setting"><a
                     
    href="${pageContext.request.contextPath}/permission/findAll">
                         <i class="fa fa-circle-o"></i> 权限管理
                   </a></li>
                   </security:authorize>
                   <security:authorize access="hasAnyRole('ROLE_ADMIN')">
                   <li id="system-setting"><a
                     
    href="${pageContext.request.contextPath}/pages/syslog-list.jsp"> <i
                        
    class="fa fa-circle-o"></i> 访问日志
                   </a></li>
                   </security:authorize>
                </ul></li>


             <li class="treeview">
                <security:authorize access="hasAnyRole('ROLE_USER')">
                   <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>
                </security:authorize>

                <ul class="treeview-menu">

                   <security:authorize access="hasAnyRole('ROLE_USER')">
                   <li id="system-setting"><a
                     
    href="${pageContext.request.contextPath}/product/findByPage">
                         <i class="fa fa-circle-o"></i> 产品管理
                   </a></li>
                   </security:authorize>

                   <security:authorize access="hasAnyRole('ROLE_USER')">
                   <li id="system-setting"><a
                     
    href="${pageContext.request.contextPath}/order/findByPage">
                         <i class="fa fa-circle-o"></i> 订单管理
                   </a></li>
                   </security:authorize>

                </ul>
             </li>

          </ul>
       </section>
       <!-- /.sidebar -->
    </aside>

    2.2   服务端控制权限

    2.2.1     为什么需要在服务端控制权限呢?

    (1)       例如普通用户jack登陆系统, 只能看到基础数据。

     

    (2)       但是,直接在浏览器输入地址,也是可以访问角色管理模块的

     

    2.2.2     方式1:JSR-250注解方式权限校验

    (1)       刚才菜单没有显示,如果直接访问地址栏,那么也会进入到具体的方法中.

    (2)       现在要解决这个问题:使用一些注解来实现权限校验。

    (3)       在讲各种注解之前,一定一定需要先配置AOP注解的支持,

    而且一定需要在springmvc.xml配置文件中配置

    (4)       <!-- 开启AOP的支持 -->

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

    (4)       完整应用

    第一步:开启Aop注解支持

     

    第二步:JSR-250注解方式权限拦截,需要添加依赖(已经完成)

    第三步:在spring-security.xml配置文件中开启JSR-250的注解支持

    <security:global-method-security jsr250-annotations="enabled"/>

    第四步:在Controller的类或者方法上添加注解@RolesAllowed

     

               

    2.2.3     方式2:SpringSecurity注解方式实现权限校验

    (1)   在spring-security.xml配置文件中开启注解支持

    <security:global-method-security secured-annotations="enabled"></security:global-method-security>

    (2)       在Controller类或者方法添加注解@Secured

     

    2.2.4     方式3:SpEL表达式方式实现权限校验

    (1)   在spring-security.xml配置文件中开启注解支持

    <!--方式3spel表达式提供的权限校验支持-->
    <security:global-method-security pre-post-annotations="enabled"></security:global-method-security>

    (2)       在Controller类或者方法添加注解@PreAuthorize

    3   系统日志功能

     

    3.1   需求

    希望系统自动记录访问后台controller的时间、方法、来访者ip等信息。

    3.2   建表

    CREATE TABLE sys_log(

        id int PRIMARY KEY,

        visitTime DATE,

        username VARCHAR2(50),

        ip VARCHAR2(30),

        method VARCHAR2(200)

    )

    3.3   domain

    public class SysLog {
        private Long id;
        private Date visitTime;
        private String username;
        private String ip;
        private String method;

    3.4   dao

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

    3.5   service

    3.5.1     接口

    public interface ISysLogService {
        void save(SysLog sysLog);
    }

    3.5.2     实现

    @Service
    @Transactional
    public class SysLogServiceImpl implements ISysLogService {
        @Autowired
        private ISysLogDao sysLogDao;
        @Override
        public void save(SysLog sysLog) {
            sysLogDao.save(sysLog);
        }
    }

    3.6   编写切面类

    3.6.1     先再web.xml配置监听器

    <listener>
       <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    然后再切面类中就可以注入HttpServletRequest对象,获取来访者ip。

    3.6.2     切面类

    package com.itheima.utils;

    import com.itheima.domain.SysLog;
    import com.itheima.service.ISysLogService;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    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;

    @Component
    @Aspect
    public class LogAop {

        // 注入request对象(需要配置监听器)
       
    @Autowired
        private HttpServletRequest request;

        @Autowired
        private ISysLogService sysLogService;

        @Around("execution(* com.itheima.controller.*Controller.*(..))")
        public Object arount(ProceedingJoinPoint pjp) {
            //1. 获取方法参数
           
    Object[] methodArgs = pjp.getArgs();
            //2. 获取正在执行的类和方法
            //pjp.getSignature().getName();
    方法名称
           
    String methodName = pjp.getSignature().toShortString();
            //3. 获取用户
           
    SecurityContext securityContext = SecurityContextHolder.getContext();
            Authentication authentication = securityContext.getAuthentication();
            User user = (User) authentication.getPrincipal();
            String username = user.getUsername();
            //4. 获取ip
           
    String remoteAddr = request.getRemoteAddr();
            //5. 封装
           
    SysLog log = new SysLog();
            log.setIp(remoteAddr);
            log.setMethod(methodName);
            log.setUsername(username);
            log.setVisitTime(new Date());

            try {
                // 放行,去到控制器处理请求的方法
               
    Object returnValue = pjp.proceed(methodArgs);
                //方法执行后增强: 记录日志
               
    sysLogService.save(log);
                return returnValue;
            } catch (Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
    }

    3.6.3       注解扫描切面类

    3.6.4     访问controller测试

    观察数据库中数据变化,

  • 相关阅读:
    WinAPI: 钩子回调函数之 GetMsgProc
    WinAPI: 钩子回调函数之 MouseProc
    WinAPI: 钩子回调函数之 CBTProc
    WinAPI: 钩子回调函数之 ShellProc
    WinAPI: 钩子回调函数之 ForegroundIdleProc
    WinAPI: 钩子回调函数之 CallWndProc
    WinAPI: 钩子回调函数之 DebugProc
    WinAPI: 钩子回调函数之 HardwareProc
    CodeIgniter入门案例之简单新闻系统三
    CodeIgniter 类库
  • 原文地址:https://www.cnblogs.com/itboxue/p/10493916.html
Copyright © 2011-2022 走看看