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测试

    观察数据库中数据变化,

  • 相关阅读:
    leetcode33. Search in Rotated Sorted Array
    pycharm 设置sublime text3 monokai主题
    django class Meta
    leetcode30, Substring With Concatenation Of All Words
    Sublime text3修改tab键为缩进为四个空格,
    sublime text3 python打开图像的问题
    安装上imesupport输入法依然不跟随的解决办法,
    sublime text3 的插件冲突弃用问题,
    sublime text3 BracketHighlighter括号匹配的设置
    windows 下wget的使用
  • 原文地址:https://www.cnblogs.com/itboxue/p/10493916.html
Copyright © 2011-2022 走看看