当前位置: 首页 > news >正文

尚好房 10_Spring Security

尚好房:Spring Security

一、Spring Security概念

前面我们已经完成了尚好房权限管理的部分相关功能,给用户分配角色,给角色分配权限,及左侧动态菜单,做好权限管理的数据准备,接下来我们要使用这些数据进行权限的相关控制。

1、认证和授权概念

现在我们需要思考2个问题:

*问题1*:在生产环境下我们如果不登录后台系统就可以完成这些功能操作吗?

答案显然是否定的,要操作这些功能必须首先登录到系统才可以。

*问题2*:是不是所有用户,只要登录成功就都可以操作所有功能呢?

答案是否定的,并不是所有的用户都可以操作这些功能。不同的用户可能拥有不同的权限,这就需要进行授权了。(用户登录之后,对每个用户进行授权,通过授权去访问系统中不同的功能–>授权)

*认证*:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。

*授权*:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

本章节就是要对后台系统进行权限控制,其本质就是对用户进行认证和授权。

2、Spring Security简介

Spring Security是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我们来简化认证和授权的过程。

官网:https://spring.io/projects/spring-security/

中文官网:https://www.w3cschool.cn/springsecurity/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1BH9U44-1661871891695)(images/10/img_001.png)]

对应的maven坐标:

<!-- spring security安全框架 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.2.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.2.7.RELEASE</version>
</dependency>

常用的权限框架除了Spring Security,还有Apache的shiro框架。

二、Spring Security集成入门

目标: 让用户访问管理后台中的资源的时候,需要输入用户名和密码进行登录

1、引入依赖

1.1、shf-parent添加版本管理

pom.xml

<spring.security.version>5.2.7.RELEASE</spring.security.version>
<!-- spring security安全框架 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${spring.security.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${spring.security.version}</version>
</dependency>

1.2、web-admin引入依赖

目前只是我们的后台管理系统需要授权与认证

pom.xml

<dependencies>
    <!-- spring security安全框架 -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
    </dependency>
</dependencies>

2、配置Spring Security Fillter

web.xml

<!-- SpringSecurity Filter -->
<!-- DelegatingFilterProxy用于整合第三方框架(代理过滤器,非真正的过滤器,真正的过滤器需要在spring的配置文件) -->
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

3、配置Spring Security

配置Spring Security有两种方式:

​ 1、xml文件配置

​ 2、java类配置

两种方式配置效果一致,当前我们使用java类配置,更加简洁,我们在web-admin项目中创建com.atguigu.config.WebSecurityConfig

package com.atguigu.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


@Configuration
@EnableWebSecurity //@EnableWebSecurity是开启SpringSecurity的默认行为
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

}

4、测试

仅需三个步骤,我们就已经集成好了Spring Security,其他的事情就可以交给Spring Security为我们处理。

启动项目

访问:http://localhost:8000/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bspeZ7xs-1661871891700)(images/10/img_002.png)]

所有资源访问受限(包括静态资源)

url自动跳转到了一个默认的登录页面(框架自带的),我们目前没有定义login页面及login controller方法。

但是当前没有账号啊!下面我们测试一个最简单的内存分配用户名密码。

4.1、内存分配用户名密码

操作类:WebSecurityConfig

重写configure(AuthenticationManagerBuilder auth)方法

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("lucy")
            .password(new BCryptPasswordEncoder().encode("123456"))
            .roles("");

}

请求:http://localhost:8000/

报错:springsecurity There is no PasswordEncoder mapped for the id "null"

需要设置加密方式

4.2、设置加密方式

/**
 * 必须指定加密方式,上下加密方式要一致
 * @return
 */
@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

登录成功,但是iframe部分页面不显示

4.3、设置允许iframe嵌套显示

默认Spring Security不允许iframe嵌套显示,我们需要设置

@Override
protected void configure(HttpSecurity http) throws Exception {
    //必须调用父类的方法,否则就不需要认证即可访问
    super.configure(http);
    //允许iframe嵌套显示
    http.headers().frameOptions().disable();
}

到目前为止,我们通过内存分配用户名密码的方式,可以访问后台页面了。

三、Spring Security集成进阶

前面我们已经完成了Spring Security的入门级配置,通过Spring Security的使用,Spring Security将我们项目中的所有资源都保护了起来,要访问这些资源必须要完成认证才能够访问。

但是这个案例中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:

1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源不需要认证也可以访问,也就是可以匿名访问。

2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。

3、直接将用户名和密码配置在了java程序中,而真实生产环境下的用户名和密码往往保存在数据库中。

现在我们需要对这些问题进行改进。

1. 自定义登录页面

1.1 创建登录页面

① 在web-admin项目中创建templates/frame/login.html页面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

    <head th:include="common/head :: head"></head>

    <body class="gray-bg">

        <div class="middle-box text-center loginscreen  animated fadeInDown">
            <div>
                <div>

                    <h1 class="logo-name"></h1>

                </div>
                <h3>欢迎使用 尚好房平台管理系统</h3>

                <form class="m-t" role="form" th:action="@{/login}" method="post">
                    <label style="color:red;" th:if="${param.error}" th:text="用户名或密码错误"></label>
                    <div class="form-group">
                        <input type="text" name="username" value="admin" class="form-control" placeholder="用户名" required="">
                    </div>
                    <div class="form-group">
                        <input type="password" name="password" value="123456" class="form-control" placeholder="密码" required="">
                    </div>
                    <button type="submit" class="btn btn-primary block full-width m-b">登 录</button>


                    <p class="text-muted text-center"> <a href="javascript:"><small>忘记密码了?</small></a> | <a href="javascript:">注册一个新账号</a>
                    </p>
                </form>
            </div>
        </div>
    </body>
</html>

② 在spring-mvc.xml中配置访问登录页面的请求映射

<mvc:view-controller path="/login" view-name="frame/login"/>

1.2 修改WebSecurityConfig配置类

在WebSecurityConfig配置类中重写如下方法:

@Override
protected void configure(HttpSecurity http) throws Exception {
    //允许iframe嵌套显示
    http.headers().frameOptions().disable();
    http
        .authorizeRequests()
        .antMatchers("/static/**","/login").permitAll()  //允许匿名用户访问的路径
        .anyRequest().authenticated()    // 其它页面全部需要验证
        .and()
        .formLogin()
        .loginPage("/login")    //用户未登录时,访问任何需要权限的资源都转跳到该路径,即登录页面,此时登陆成功后会继续跳转到第一次访问的资源页面(相当于被过滤了一下)
        .defaultSuccessUrl("/") //登录认证成功后默认转跳的路径
        .and()
        .logout()
        .logoutUrl("/logout")   //退出登陆的路径,指定spring security拦截的注销url,退出功能是security提供的
        .logoutSuccessUrl("/login");//用户退出后要被重定向的url
    //关闭跨域请求伪造
    http.csrf().disable();
}

2. 使用数据库表中的用户名和密码

2.1 注释掉内存分配用户名和密码

/*@Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("lucy")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("");
}*/

2.2 持久层

2.2.1 AdminMapper接口

service-acl项目的com.atguigu.mapper.AdminMapper接口中新增方法

Admin getByUsername(String username);
2.2.2 AdminMapper.xml映射配置文件

service-acl项目的resources/mappers/AdminMapper.xml中新增

<select id="getByUsername" resultType="Admin">
    <include refid="columns"></include>
    from acl_admin
    where
    username = #{username}
    and is_deleted = 0
</select>

2.3 业务层

2.3.1 AdminService接口

service-api项目的com.atguigu.service.AdminService接口中新增方法

Admin getByUsername(String username);
2.3.2 AdminServiceImpl实现类

service-acl项目的com.atguigu.service.impl.AdminServiceImpl实现类中新增方法

@Override
public Admin getByUsername(String username) {
    return adminMapper.getByUsername(username);
}

2.4 UserDetailsService接口的实现类

Spring Security支持通过实现UserDetailsService接口的方式来提供用户认证授权信息

2.4.1 新建UserDetailsServiceImpl实现类

我们在web-admin项目中创建com.atguigu.config.UserDetailsServiceImpl实现类

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Reference
    private AdminService adminService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查找用户
        Admin admin =adminService.getByUsername(username);
        if(null == admin) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        //校验密码,操作权限目前用空的
        return new User(username,admin.getPassword(),
                AuthorityUtils.commaSeparatedStringToAuthorityList(""));
    }
}
2.4.2 添加用户时对密码进行加密

前面添加用户是我们没有对密码进行加密处理,现在改造。删除未加密的数据记录,重新创建用户信息

修改com.atguigu.controller.AdminControllersave方法

@Autowired
private PasswordEncoder passwordEncoder;
@PostMapping("/save")
public String save(Admin admin, Model model){
    //设置密码
    admin.setPassword(passwordEncoder.encode(admin.getPassword()));
    adminService.insert(admin);
    return successPage(model,"新增用户成功");
}

3. 左侧动态菜单

之前我们获取左侧动态菜单的时候,是写死用户为admin,现在可以用Spring Security获取登录的用户

修改web-admin项目中的com.atguigu.controller.IndexController类的index()方法

@GetMapping("/")
public String index(Model model){
    //获取当前登录的用户
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    User user = (User) authentication.getPrincipal();
    Admin admin = adminService.getByUsername(user.getUsername());
    //查询用户的权限列表
    List<Permission> permissionList = permissionService.findMenuPermissionByAdminId(admin.getId());
    model.addAttribute("admin",admin);
    model.addAttribute("permissionList",permissionList);
    return PAGE_INDEX;
}

4. 用户授权

4.1 获取用户权限

4.1.1 持久层
4.1.1.1 PermissionMapper接口

service-acl项目的com.atguigu.mapper.PermissionMapper接口中新增

/**
* 查询用户的操作权限
* @param adminId
* @return
*/
List<String> findCodePermissionListByAdminId(Long adminId);

/**
* 查询所有操作权限
* @return
*/
List<String> findAllCodePermission();
4.1.1.2 PermissionMapper.xml映射配置文件

service-acl项目中的resources/mappers/PermissionMapper.xml中新增

<select id="findAllCodePermission" resultType="string">
    select code from acl_permission where is_deleted=0
</select>

<select id="findCodePermissionListByAdminId" resultType="string">
    SELECT code
    FROM acl_permission
    WHERE id IN (
    SELECT permission_id FROM acl_role_permission WHERE role_id IN (
    SELECT role_id FROM acl_admin_role WHERE admin_id=#{adminId} AND is_deleted=0
    ) AND is_deleted=0
    ) AND TYPE=2 AND is_deleted=0
</select>
4.1.2 业务层
4.1.2.1 PermissionService接口

service-api项目的com.atguigu.service.PermissionService接口中新增

/**
* 查询用户的操作权限
* @param adminId
* @return
*/
List<String> findCodePermissionListByAdminId(Long adminId);
4.1.2.2 PermissionServiceImpl实现类

service-acl项目的com.atguigu.service.impl.PermissionServiceImpl实现类中新增

@Override
public List<String> findCodePermissionListByAdminId(Long adminId) {
    //判断是否是超级管理员
    if (adminId == 1) {
        //拥有所有权限
        return permissionMapper.findAllCodePermission();
    }
    return permissionMapper.findCodePermissionListByAdminId(adminId);
}

4.2 将用户权限配置到Spring Security中

修改web-admin项目的com.atguigu.config.UserDetailsServiceImpl

@Component
public class UserDetailServiceImpl implements UserDetailsService {
    @Reference
    private AdminService adminService;
    @Reference
    protected PermissionService permissionService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Admin admin = adminService.getByUsername(username);
        if(null == admin) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        //获取用户权限列表
        List<String> codePermissionList = permissionService.findCodePermissionListByAdminId(admin.getId());

        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
        for (String code : codePermissionList) {
            if(StringUtils.isEmpty(code)) {
                continue;
            }
            grantedAuthorityList.add(new SimpleGrantedAuthority(code));
        }
        return new User(username,admin.getPassword(), grantedAuthorityList);
    }
}

4.3 Controller方法权限控制

**目标:**给各个Controller的方法指定对应的操作权限,以角色管理增删改查等为例

4.3.1 开启Controller方法权限控制

修改web-admin项目中的com.atguigu.config.WebSecurityConfig配置类,添加下述注解

@EnableGlobalMethodSecurity(prePostEnabled = true)
4.3.2 给Controller方法添加权限注解

修改web-admin项目中的com.atguigu.controller.RoleController

@Controller
@RequestMapping("/role")
public class RoleController extends BaseController {
    @Reference
    private RoleService roleService;
    @Reference
    private PermissionService permissionService;
    private final static String LIST_ACTION = "redirect:/role";
    private static final String PAGE_ASSIGN_SHOW = "role/assignShow";

    @PreAuthorize("hasAnyAuthority('role.show')")
    @RequestMapping
    public String index(@RequestParam Map conditions, Model model){
        if(!conditions.containsKey("pageNum")) {
            conditions.put("pageNum", 1);
        }
        if(!conditions.containsKey("pageSize")) {
            conditions.put("pageSize", 10);
        }

        PageInfo<Role> pageInfo = roleService.findPage(conditions);
        model.addAttribute("page", pageInfo);
        model.addAttribute("conditions", conditions);
        return "role/index";
    }

    @PreAuthorize("hasAnyAuthority('role.create')")
    @PostMapping("/saveRole")
    public String saveRole(Role role, Model model){
        roleService.insert(role);
        return successPage(model,"添加角色成功");
    }

    @PreAuthorize("hasAnyAuthority('role.show')")
    @GetMapping("/findById/{id}")
    public String findRoleById(@PathVariable("id") Long id,Model model){
        Role role = roleService.getById(id);
        model.addAttribute("role",role);
        return "role/edit";
    }

    @PreAuthorize("hasAnyAuthority('role.edit')")
    @PostMapping("/updateRole")
    public String updateRole(Role role,Model model){
        roleService.update(role);
        return successPage(model,"更新角色成功");
    }

    @PreAuthorize("hasAnyAuthority('role.delete')")
    @GetMapping("/delete/{id}")
    public String deleteRoleById(@PathVariable("id") Long id){
        roleService.delete(id);
        return LIST_ACTION;
    }

    @PreAuthorize("hasAnyAuthority('role.assgin')")
    @GetMapping("/assignShow/{roleId}")
    public String assignShow(@PathVariable("roleId") Long roleId,Model model){
        List<Map<String, Object>> zNodes = permissionService.findPermissionByRoleId(roleId);
        model.addAttribute("zNodes", JSON.toJSONString(zNodes));
        model.addAttribute("roleId",roleId);
        return PAGE_ASSIGN_SHOW;
    }

    @PreAuthorize("hasAnyAuthority('role.assgin')")
    @PostMapping("/assignPermission")
    public String assignPermission(@RequestParam("roleId") Long roleId,
                                   @RequestParam("permissionIds") List<Long> permissionIds,
                                   Model model){
        permissionService.saveRolePermission(roleId,permissionIds);
        return successPage(model,"设置角色权限成功");
    }
}

4.4 测试

目前已admin登录,这些权限都有,点击角色管理相关功能都能正常访问。使用Admin设置某个用户只有查看角色的权限

然后admin退出登录,使用只有查看角色权限的用户登录,观察操作效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b5Oi2V8T-1661871891703)(images/10/img_003.png)]

不能访问了,提示403错误状态,表示没有访问权限。

4.5 优化用户体验

上面这样提示很不友好,我们自定义提示页面

4.5.1 创建提示页面

web-admin项目中创建templates/frame/auth.html

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body style="position: relative;">
        <div style="text-align:center;margin-top: 100px;font-size: 20px;">
            <strong>没有权限</strong>
        </div>
    </body>
</html>
4.5.2 在spring-mvc.xml中配置访问提示页面

web-admin项目中的resources/spring/spring-mvc.xml中新增

<!--权限页面-->
<mvc:view-controller path="/auth" view-name="frame/auth"/>
4.5.3 实现AccessDeniedHandler接口

web-admin项目中创建com.atguigu.config.AtguiguAccessDeniedHandler

public class AtguiguAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.sendRedirect("/auth");
    }
}
4.5.4 在Spring Security配置类中配置AtguiguAccessDeniedHandler

修改web-admin项目中的com.atguigu.config.WebSecurityConfig

@Override
protected void configure(HttpSecurity http) throws Exception {
    //允许iframe嵌套显示
    http.headers().frameOptions().disable();
    http
        .authorizeRequests()
        .antMatchers("/static/**","/login").permitAll()  //允许匿名用户访问的路径
        .anyRequest().authenticated()    // 其它页面全部需要验证
        .and()
        .formLogin()
        .loginPage("/login")    //用户未登录时,访问任何需要权限的资源都转跳到该路径,即登录页面,此时登陆成功后会继续跳转到第一次访问的资源页面(相当于被过滤了一下)
        .defaultSuccessUrl("/") //登录认证成功后默认转跳的路径,意思时admin登录后也跳转到/user
        .and()
        .logout()
        .logoutUrl("/logout")   //退出登陆的路径,指定spring security拦截的注销url,退出功能是security提供的
        .logoutSuccessUrl("/login");//用户退出后要被重定向的url
    //关闭跨域请求伪造
    http.csrf().disable();
	
    //指定自定义的访问拒绝处理器
    http.exceptionHandling().accessDeniedHandler(new AtguiguAccessDeniedHandler());
}

4.6 优化之后的测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-etCivD6l-1661871891705)(images/10/img_004.png)]

5. 页面功能按钮权限控制

上面我们完成了controller层方法的权限,现在我们要控制页面按钮的权限,如:角色管理上面只有查看权限,那么页面新增、修改、删除、分配权限按都不显示。

怎么实现呢?其实Spring Security已经给我们封装好了标签库,我们直接使用即可。

5.1 shf-parent工程管理依赖版本

pom.xml

<thymeleaf-springsecurity5.version>3.0.4.RELEASE</thymeleaf-springsecurity5.version>
<!--用于springsecurity5标签-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>${thymeleaf-springsecurity5.version}</version>
</dependency>

5.2 web-admin引入依赖

<!--用于springsecurity5标签-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

5.3 Thymeleaf模板引擎配置spring security 标签支持

修改web-admin项目的resources/spring/spring-mvc.xml配置文件,在Thymeleaf的模板引擎配置spring security 标签支持

<!--配置模板引擎-->
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
    <!--引用视图解析器-->
    <property name="templateResolver" ref="templateResolver"></property>
    <!-- 添加spring security 标签支持:sec -->
    <property name="additionalDialects">
        <set>
            <bean class="org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect" />
        </set>
    </property>
</bean>

5.4 页面按钮控制

① 在html文件里面声明使用spring-security标签

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

② 在相关按钮上使用标签

<button type="button" class="btn btn-sm btn-primary create" sec:authorize="hasAuthority('role.create')">新增</button>

<a class="edit" th:attr="data-id=${item.id}" sec:authorize="hasAuthority('role.edit')">修改</a>
<a class="delete" th:attr="data-id=${item.id}" sec:authorize="hasAuthority('role.delete')">删除</a>
<a class="assgin" th:attr="data-id=${item.id}" sec:authorize="hasAuthority('role.assgin')">分配权限</a>

③ 页面完整代码

<!DOCTYPE html>
<html lang="en"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
      xmlns:th="http://www.thymeleaf.org">
    <head th:include="common/head :: head"></head>
    <body class="gray-bg">
        <form id="ec" th:action="@{/role}" method="get">
            <div class="wrapper wrapper-content animated fadeInRight">
                <div class="row">
                    <div class="col-sm-12">
                        <div class="ibox float-e-margins">
                            <div class="ibox-content">
                                <table class="table form-table margin-bottom10">
                                    <tr>
                                        <td>
                                            <input type="text" name="roleName" th:value="${#maps.containsKey(conditions, 'roleName')} ? ${conditions.roleName} : ''" placeholder="角色名称" class="input-sm form-control"/>
                                        </td>
                                    </tr>
                                </table>
                                <div>
                                    <button type="button" class="btn btn-sm btn-primary" onclick="javascript:document.forms.ec.pageNum.value=1;document.forms.ec.submit();">搜索</button>
                                    <button type="button" onclick="addRole()"
                                            sec:authorize="hasAuthority('role.create')"
                                            class="btn btn-sm btn-primary">新增</button>
                                    <button type="button" id="loading-example-btn" onclick="javascript:window.location.reload();" class="btn btn-white btn-sm">刷新</button>
                                </div>
                                <table class="table table-striped table-bordered table-hover dataTables-example">
                                    <thead>
                                        <tr>
                                            <th>序号</th>
                                            <th>角色名称</th>
                                            <th>角色编码</th>
                                            <th>描述</th>
                                            <th>创建时间</th>
                                            <th>操作 </th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr class="gradeX" th:each="item,it : ${page.list}">
                                            <td class="text-center" th:text="${it.count}">11</td>
                                            <td th:text="${item.roleName}">22</td>
                                            <td th:text="${item.roleCode}">33</td>
                                            <td th:text="${item.description}">33</td>
                                            <td th:text="${#dates.format(item.createTime,'yyyy-MM-dd HH:mm:ss')}" >33</td>
                                            <td class="text-center">
                                                <a class="edit" th:attr="data-id=${item.id}"
                                                   sec:authorize="hasAuthority('role.edit')"
                                                   onclick="editRole()" th:href="@{/role/findById/}+${item.id}">修改</a>
                                                <a class="delete"
                                                   sec:authorize="hasAuthority('role.delete')"
                                                   th:attr="data-id=${item.id}" onclick="deleteRole()" th:href="@{/role/delete/}+${item.id}">删除</a>
                                                <a class="assgin"
                                                   sec:authorize="hasAuthority('role.assgin')"
                                                   th:attr="data-id=${item.id}">分配权限</a>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table>
                                <div class="row" th:include="common/pagination :: pagination"></div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </form>
        <script th:inline="javascript">
            function addRole(){
                opt.openWin("/role/create","新增",630,430)
            }

            function editRole(){
                //阻止默认
                event.preventDefault()

                opt.openWin(event.target.href,'修改',580,430);
            }

            function deleteRole(){
                //阻止默认
                event.preventDefault()

                opt.confirm(event.target.href)
            }

            $(".assgin").on("click",function () {
                var id = $(this).attr("data-id");
                opt.openWin("/role/assignShow/"+id,'修改',580,430);
            });
        </script>
    </body>
</html>

④ 测试

如果用户没有相应的权限,那么按钮会消失

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sy23ASoZ-1661871891707)(images/10/img_005.png)]

相关文章:

  • JDK RMI探索与使用--序列化
  • Self-supervised Low Light Image Enhancement and Denoising 论文阅读笔记
  • hive窗口函数(开窗函数)
  • SpringMVC:整合SSM
  • 【每日一题】路径总和 III
  • 【Vue】基础系列(三三)指令语法-事件及其修饰符,动态样式,v-model的用法,数据持久化存在本地localStorage
  • 01_JSON的理解
  • 3D感知技术(3)双目立体视觉测距
  • spring学习第二天_Spring Ioc(1)
  • 22-08-30 西安JUC(03) Callable接口、阻塞队列4套方法、ThreadPool线程池
  • React(8)-组件ref
  • 2022/8/30
  • picoCTF - Day 1 - Warm up
  • 前端面试题之组件
  • 自己动手写编译器:词法解析的系统化研究
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 2017-08-04 前端日报
  • C++入门教程(10):for 语句
  • css的样式优先级
  • Git 使用集
  • Github访问慢解决办法
  • Java到底能干嘛?
  • php面试题 汇集2
  • Promise面试题,控制异步流程
  • Redis 懒删除(lazy free)简史
  • SpriteKit 技巧之添加背景图片
  • Vue官网教程学习过程中值得记录的一些事情
  • 对象引论
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 盘点那些不知名却常用的 Git 操作
  • 前端js -- this指向总结。
  • 悄悄地说一个bug
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • Semaphore
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • (23)Linux的软硬连接
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (五)IO流之ByteArrayInput/OutputStream
  • (转)创业的注意事项
  • (转载)hibernate缓存
  • .dwp和.webpart的区别
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET 8.0 中有哪些新的变化?
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .net2005怎么读string形的xml,不是xml文件。
  • .Net8 Blazor 尝鲜
  • .NET框架
  • .net实现客户区延伸至至非客户区
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • .NET中使用Protobuffer 实现序列化和反序列化