1. We will start by creating the list page, as we have done with all of the prior entity CRUD pages.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- Core CSS - Include with every page --> <link type="text/css" rel="stylesheet" href="../../resources/css/bootstrap-3.1.1.min.css" data-th-href="@{/resources/css/bootstrap-3.1.1.min.css}" /> <link type="text/css" rel="stylesheet" href="../../resources/font-awesome/css/font-awesome.css" data-th-href="@{/resources/font-awesome/css/font-awesome.css}" /> <!-- SB Admin CSS - Include with every page --> <link type="text/css" rel="stylesheet" href="../../resources/css/sb-admin.css" data-th-href="@{/resources/css/sb-admin.css}" /> <style> .no-border-on-me>thead>tr>th, .no-border-on-me>tbody>tr>th, .no-border-on-me>tfoot>tr>th, .no-border-on-me>thead>tr>td, .no-border-on-me>tbody>tr>td, .no-border-on-me>tfoot>tr>td { border-top-style: none; border-bottom-style: none; } </style> <style> .dropdown-menu { min-width: 0px; } </style> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <title data-th-text="#{permission.list.page.title}">Title</title> </head> <body> <div id="wrapper"> <!-- /#wrapper --> <div data-th-replace="fragments/sb-admin :: top-nav"></div> <div data-th-replace="fragments/sb-admin :: vert-nav"></div> <div id="page-wrapper"> <div class="row"> <div class="col-xs-12"> <h4 class="page-header" data-th-text="#{permission.list.table.title}">Configured Users</h4> <div class="table responsive"> <table class="table table-striped table-bordered table-hover"> <thead> <tr> <th class="col-xs-1" data-th-text="#{permission.list.id.label}">Id</th> <th class="col-xs-4" data-th-text="#{permission.list.permission.label}">PermissionName</th> <th class="col-xs-1" data-th-each="role : ${allRoles}"> <span data-th-text="${role.rolename}"></span> </th> <th class="col-xs-1" data-th-text="#{permission.list.actions.label}">Action</th> </tr> </thead> <tbody> <tr data-th-each="permission : ${permissions}"> <td data-th-text="${permission.id}">1</td> <td data-th-text="${permission.permissionname}">perm1</td> <td th:if="${allRoles[0]}"> <input th:if="${#lists.contains(permission.permRoles, allRoles[0])}" type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </td> <td th:if="${#lists.size(allRoles)} > 1"> <input th:if="${#lists.contains(permission.permRoles, allRoles[1])}" type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </td> <td th:if="${#lists.size(allRoles)} > 2"> <input th:if="${#lists.contains(permission.permRoles, allRoles[2])}" type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </td> <td th:if="${#lists.size(allRoles)} > 3"> <input th:if="${#lists.contains(permission.permRoles, allRoles[3])}" type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </td> <td th:if="${#lists.size(allRoles)} > 4"> <input th:if="${#lists.contains(permission.permRoles, allRoles[4])}" type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </td> <td th:if="${#lists.size(allRoles)} > 5"> <input th:if="${#lists.contains(permission.permRoles, allRoles[5])}" type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </td> <td> <div class="btn-group"> <button class="btn btn-warning btn-xs dropdown-toggle" type="button" data-toggle="dropdown" data-th-text="#{permission.list.actions.label}"> Actions<span class="caret"></span> </button> <ul class="dropdown-menu"> <li> <a href="#" data-th-href="@{/permission/edit(id=${permission.id})}"> <span class="glyphicon glyphicon-pencil"></span> <span data-th-text="#{button.label.edit}">edit</span> </a> </li> <li> <a href="#" data-th-href="@{/permission/delete(id=${permission.id},phase=stage)}"> <span class="glyphicon glyphicon-trash"></span> <span data-th-text="#{button.label.delete}">delete</span> </a> </li> </ul> </div> </td> </tr> <tr data-th-remove="all"> <td>2</td> <td>perm_test</td> <td>role1</td> <td> <div class="btn-group"> <button class="btn btn-warning btn-xs dropdown-toggle" type="button" data-toggle="dropdown" data-th-text="#{permission.list.actions.label}"> Actions<span class="caret"></span> </button> <ul class="dropdown-menu"> <li> <a href="#"> <span class="glyphicon glyphicon-pencil"></span> <span data-th-text="#{button.label.edit}">edit</span> </a> </li> <li> <a href="#"> <span class="glyphicon glyphicon-trash"></span> <span data-th-text="#{button.label.delete}">delete</span> </a> </li> </ul> </div> </td> </tr> </tbody> </table> </div> <form class="form" action="#" data-th-action="@{/permission/add}" data-th-object="${permissionDTO}" method="post"> <div class="table responsive"> <table class="no-border-on-me table "> <thead> <tr> <th class="col-xs-2"></th> <th class="col-xs-4" data-th-text="#{permission.list.permission.label}">Perm</th> <th class="col-xs-6" data-th-text="#{permission.list.actions.label}">Action</th> </tr> </thead> <tbody> <tr> <td><input type="text" hidden="hidden" data-th-field="*{id}"></input></td> <td><input class="form-control" type="text" data-th-field="*{permissionname}" placeholder="Permission Name"></input></td> <td> <button type="submit" class="btn btn-primary" data-th-text="#{button.label.add}">Add Permission</button> </td> </tr> <tr> <td class="col-xs-2"></td> <td class="col-xs-4 text-danger" data-th-if="${#fields.hasErrors('permissionname')}" data-th-errors="*{permissionname}">permission error</td> <td class="col-xs-6"></td> </tr> </tbody> </table> </div> </form> <div class="alert alert-danger alert-dismissable" th:if="${error != null}"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> <h4 data-th-text="${error}">Error!</h4> </div> <div class="alert alert-success alert-dismissable" th:if="${message != null}"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> <h4 data-th-text="${message}">Success!</h4> </div> </div> <!-- /.col-lg-12 --> </div> <!-- /.row --> </div> <!-- page wrapper --> </div> <!-- /#wrapper --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script type="text/javascript" src="../../resources/js/jquery-1.11.0.min.js" data-th-src="@{/resources/js/jquery-1.11.0.min.js}"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script type="text/javascript" src="../../resources/js/bootstrap-3.1.1.min.js" data-th-src="@{/resources/js/bootstrap-3.1.1.min.js}"></script> <!-- Core Scripts - Include with every page --> <script type="text/javascript" src="../../resources/js/plugins/metisMenu/jquery.metisMenu.js" data-th-src="@{/resources/js/plugins/metisMenu/jquery.metisMenu.js}"></script> <!-- SB Admin Scripts - Include with every page --> <script type="text/javascript" src="../../resources/js/sb-admin.js" data-th-src="@{/resources/js/sb-admin.js}"></script> </body> </html>There are several lines highlighted in the code above. Here is a bit more detail about those highlighted lines:
- Lines 68 - 70: In this block of code we are displaying all of the roles as column headers. This section should be limited to 6 roles as the detail below can only display six roles. From a functional perspective, I didn't plan on having more than three roles, with a possibility for a fourth. So, I figured a maximum of six in the display was more than enough. This header should really have a cap of six too...so this is a refactoring opportunity for the future.
- Lines 78, 82, 86, 90, 94, 98: First, these columns (td tags) will only be displayed if there is a header/role for this particular column. If there are only three roles in the system, then only three role headers will print, resulting in three role columns. These lines will be conditionally displayed as we loop through each of the permissions in the permissions table.
- Lines 78, 82, 86, 90, 94, 98: If the permission being displayed is associated with a role in the column (td tag), then a disabled check box will be displayed, otherwise nothing will be displayed in that column. Each td block corresponds to a column, and each column corresponds to a specific role. If you spend some time looking at this code, the functionality should become clear...if not, please leave a comment and I will try to elaborate.
- Starting on line 155 is the form used to add a permission to the system. This line also refers to the DTO used for adding the permission. The line referring to the name of the permission is line number 168.
2. Next we move onto the Permission edit page.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- Core CSS - Include with every page --> <link type="text/css" rel="stylesheet" href="../../resources/css/bootstrap-3.1.1.min.css" data-th-href="@{/resources/css/bootstrap-3.1.1.min.css}" /> <link type="text/css" rel="stylesheet" href="../../resources/font-awesome/css/font-awesome.css" data-th-href="@{/resources/font-awesome/css/font-awesome.css}" /> <!-- SB Admin CSS - Include with every page --> <link type="text/css" rel="stylesheet" href="../../resources/css/sb-admin.css" data-th-href="@{/resources/css/sb-admin.css}" /> <style> .no-border-on-me>thead>tr>th, .no-border-on-me>tbody>tr>th, .no-border-on-me>tfoot>tr>th, .no-border-on-me>thead>tr>td, .no-border-on-me>tbody>tr>td, .no-border-on-me>tfoot>tr>td { border-top-style: none; border-bottom-style: none; } </style> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <title data-th-text="#{permission.edit.page.title}">Title</title> </head> <body> <div id="wrapper"> <!-- /#wrapper --> <div data-th-replace="fragments/sb-admin :: top-nav"></div> <div data-th-replace="fragments/sb-admin :: vert-nav"></div> <div id="page-wrapper"> <div class="row"> <div class="col-lg-12"> <h4 class="page-header" data-th-text="#{permission.edit.head.title}">Edit Permission</h4> <div class="col-sm-2"></div> <div class="col-sm-6"> <form class="form-horizontal" action="#" data-th-action="@{/permission/edit}" data-th-object="${permissionDTO}" method="post"> <div class="form-group"> <label class="col-sm-5 control-label" data-th-text="#{permission.list.permission.label}">Permission</label> <div class="col-sm-7"> <input type="text" hidden="hidden" data-th-value="*{id}" data-th-field="*{id}" ></input> <input type="text" class="form-control" data-th-value="*{permissionname}" data-th-field="*{permissionname}" ></input> </div> </div> <div class="form-group" th:each="role : ${allRoles} "> <label class="col-sm-5 control-label" data-th-text="${role.rolename}">Role 1</label> <div class="col-sm-7"> <input type="checkbox" th:field="*{permRoles}" th:value="${role.id}"></input> </div> </div> <div class="form-group"> <div class="col-sm-offset-5 col-sm-7" > <button type="submit" class="btn btn-primary" name="action" data-th-value="#{button.action.save}" data-th-text="#{button.label.save}" >Save</button> <button type="submit" class="btn btn-default active" name="action" data-th-value="#{button.action.cancel}" data-th-text="#{button.label.cancel}">Cancel</button> </div> </div> <div class="form-group"> <div class="col-sm-offset-5 col-sm-7" > <p class="text-danger" data-th-if="${#fields.hasErrors('permissionname')}" data-th-errors="*{permissionname}">type error</p> </div> </div> </form> </div> <div class="col-sm-4"></div> </div> <!-- /.col-lg-12 --> </div> <!-- /.row --> </div> <!-- page wrapper --> </div> <!-- /#wrapper --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script type="text/javascript" src="../../resources/js/jquery-1.11.0.min.js" data-th-src="@{/resources/js/jquery-1.11.0.min.js}"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script type="text/javascript" src="../../resources/js/bootstrap-3.1.1.min.js" data-th-src="@{/resources/js/bootstrap-3.1.1.min.js}"></script> <!-- Core Scripts - Include with every page --> <script type="text/javascript" src="../../resources/js/plugins/metisMenu/jquery.metisMenu.js" data-th-src="@{/resources/js/plugins/metisMenu/jquery.metisMenu.js}"></script> <!-- SB Admin Scripts - Include with every page --> <script type="text/javascript" src="../../resources/js/sb-admin.js" data-th-src="@{/resources/js/sb-admin.js}"></script> </body> </html>Note the section highlighted in blue above. Recall that besides the DTO object passed to this page as part of the HTTP GET request, the list of all roles in the system is also passed. In the highlighted rows above, all of the roles in the system are displayed as part of the for-each loop. For each of these roles, a check box is displayed. If the role is associated with the permission being edited, then a check is placed in the box, otherwise the checkbox is left empty.
The other code in this edit page should look similar to all of the other edit pages we have created on this blog.
3. Lastly, we have the Permission delete page.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- Core CSS - Include with every page --> <link type="text/css" rel="stylesheet" href="../../resources/css/bootstrap-3.1.1.min.css" data-th-href="@{/resources/css/bootstrap-3.1.1.min.css}" /> <link type="text/css" rel="stylesheet" href="../../resources/font-awesome/css/font-awesome.css" data-th-href="@{/resources/font-awesome/css/font-awesome.css}" /> <!-- SB Admin CSS - Include with every page --> <link type="text/css" rel="stylesheet" href="../../resources/css/sb-admin.css" data-th-href="@{/resources/css/sb-admin.css}" /> <style> .no-border-on-me>thead>tr>th, .no-border-on-me>tbody>tr>th, .no-border-on-me>tfoot>tr>th, .no-border-on-me>thead>tr>td, .no-border-on-me>tbody>tr>td, .no-border-on-me>tfoot>tr>td { border-top-style: none; border-bottom-style: none; } </style> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <title data-th-text="#{permission.delete.page.title}">Title</title> </head> <body> <div id="wrapper"> <!-- /#wrapper --> <div data-th-replace="fragments/sb-admin :: top-nav"></div> <div data-th-replace="fragments/sb-admin :: vert-nav"></div> <div id="page-wrapper"> <div class="row"> <div class="col-lg-12"> <h4 class="page-header" data-th-text="#{permission.delete.head.title}">Delete Strategy</h4> <div class="col-sm-2"></div> <div class="col-sm-6"> <form class="form-horizontal" action="#" method="get"> <div class="form-group"> <label class="col-sm-5 control-label" data-th-text="#{permission.list.id.label}">Permission Id</label> <div class="col-sm-7"> <input type="text" class="form-control" data-th-value="${permission.id}" disabled="disabled" ></input> </div> </div> <div class="form-group"> <label class="col-sm-5 control-label" data-th-text="#{permission.list.permission.label}">Permission Name</label> <div class="col-sm-7"> <input type="text" class="form-control" data-th-value="${permission.permissionname}" disabled="disabled" ></input> </div> </div> <div class="form-group" th:each="role : ${permission.permRoles} "> <label class="col-sm-5 control-label" data-th-text="${role.rolename}">Role 1</label> <div class="col-sm-7"> <input type="checkbox" name="option1" checked="checked" disabled="disabled"></input> </div> </div> </form> <div class="form-horizontal"> <div class="form-group"> <label class="col-sm-5 control-label"></label> <div class="col-sm-7" > <a href="#" data-th-href="@{/permission/delete(id=${permission.id},phase=#{button.action.delete} )}"> <button type="button" class="btn btn-primary" data-th-text="#{button.label.delete}">Delete</button></a> <a href="#" data-th-href="@{/permission/delete(id=${permission.id},phase=#{button.action.cancel} )}"> <button type="button" class="btn btn-default active" data-th-text="#{button.label.cancel}">Cancel</button></a> </div> </div> </div> </div> <div class="col-sm-4"></div> </div> <!-- /.col-lg-12 --> </div> <!-- /.row --> </div> <!-- page wrapper --> </div> <!-- /#wrapper --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script type="text/javascript" src="../../resources/js/jquery-1.11.0.min.js" data-th-src="@{/resources/js/jquery-1.11.0.min.js}"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script type="text/javascript" src="../../resources/js/bootstrap-3.1.1.min.js" data-th-src="@{/resources/js/bootstrap-3.1.1.min.js}"></script> <!-- Core Scripts - Include with every page --> <script type="text/javascript" src="../../resources/js/plugins/metisMenu/jquery.metisMenu.js" data-th-src="@{/resources/js/plugins/metisMenu/jquery.metisMenu.js}"></script> <!-- SB Admin Scripts - Include with every page --> <script type="text/javascript" src="../../resources/js/sb-admin.js" data-th-src="@{/resources/js/sb-admin.js}"></script> </body> </html>Similar to the edit page, the highlighted section above loops through all of the roles associated with this permission and displays only this subset of roles on this delete page.
4. Screenshots of the Permission CRUD pages are shown below.
permission-list.html |
permission-edit.html |
permission-delete.html |
We now have working CRUD functionality for our Permission Entity. In the next development post we will look at session timeout and login redirection.
Code at GitHub: https://github.com/dtr-trading/spring-ex17-crud-perm
4 comments:
Hi dave, thank you for your tutorial.
I have this Error when I try to get all users from my database via RestController (Json format) :
[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":[{"id":1,"username":"admin","password":"password","role":{"id":1,"rolename":"ROLE_ADMIN","userRoles":
Hi Anas,
Was there a Java error? This just looks like JSON output, not a Java stack trace.
Thanks,
Dave
Hi dave, I deploy your spring-ex17-crud-perm and initialized the mysql database.
When I use user admin, I can access to all urls.
But when I use user trader, which has the same permisssions as admin, to request the url 'http://localhost:8080/oas/user/', it report an error:403 - Not Authorized.
It seems that the @PreAuthorize( "denyAll" ) annotation not work .
Hi Dave,
For permission edit page, when I assign the role to permission. Role is not blinded to permission, Do you have any solution ?
Post a Comment