In rights management, role authorization and authentication belong to the key modules in the rights module. Role authorization is the act of assigning the menu resources that the role can operate to the designated role. The role authentication is instant when the user plays the designated role and logs in to the system. The operation of resource permission verification is explained here, so how should it be implemented in the code?
Role authorization and authentication methods
- Front-end page display control
- Back-end permission access control
The most complete java learning materials→q 1080355292 (group password: 1010)
Case practice
role authorization
tree data display
After completing the basic crud function of the role record, the role authorization function is implemented next. Here, the role authorization is implemented first to complete the display function of the resources to be authorized. For the display of resources, the open source tree plugin is used here ztree.
Resource data query backend implementation
The resource data format reference displayed by the front-end ztree here.
- ModuleMapper.xml
<select id="queryAllModules" resultType="com.xxxx.crm.dto.TreeDto"> select id, IFNULL(parent_id,0) as pId, module_name AS name from t_module where is_valid=1 </select>
- ModuleService.java
public List<TreeDto> queryAllModules(){ return moduleMapper.queryAllModules(); }
- ModuleController.java
@RequestMapping("queryAllModules") @ResponseBody public List<TreeDto> queryAllModules(){ return moduleService.queryAllModules(); }
Resource data ztree display
- role.js add authorized click event
//header toolbar events table.on('toolbar(roles)', function(obj){ var checkStatus = table.checkStatus(obj.config.id); switch(obj.event){ case "add": openAddOrUpdateRoleDialog(); break; case "grant": openAddGrantDailog(checkStatus.data); break; }; }); function openAddGrantDailog(datas){ if(datas.length==0){ layer.msg("Please select a role record to be authorized!", {icon: 5}); return; } if(datas.length>1){ layer.msg("Batch role authorization is not currently supported!", {icon: 5}); return; } var url = ctx+"/role/toAddGrantPage?roleId="+datas[0].id; var title="role management-role authorization"; layui.layer.open({ title : title, type : 2, area:["600px","280px"], maxmin:true, content : url }); }
- RoleController.java adds view forwarding method
@RequestMapping("toAddGrantPage") public String toAddGrantPage(Integer roleId,Model model){ model.addAttribute("roleId",roleId); return "role/grant"; }
- Prepare to display resource data template
Add the grant.ftl template file to the views/role directory
<html> <head> <link rel="stylesheet" href="${ctx}/static/js/zTree_v3-3.5.32/css/zTreeStyle/zTreeStyle.css" type="text/css"> <script type="text/javascript" src="${ctx}/static/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script> <script type="text/javascript" src="${ctx}/static/js/zTree_v3-3.5.32/js/jquery.ztree.core.js"></script> <script type="text/javascript" src="${ctx}/static/js/zTree_v3-3.5.32/js/jquery.ztree.excheck.js"></script> </head> <body> <div id="test1" class="ztree"></div> <input id="roleId" value="${roleId!}" type="hidden"> <script type="text/javascript"> var ctx="${ctx}"; </script> <script type="text/javascript" src="${ctx}/static/js/role/grant.js"></script> </body> </html>
- add grant.js
var zTreeObj; $(function () { loadModuleInfo(); }); function loadModuleInfo() { $.ajax({ type:"post", url:ctx+"/module/queryAllModules" dataType:"json", success:function (data) { // Parameter configuration of zTree, please refer to the API documentation for in-depth use (detailed explanation of setting configuration) var setting = { data: { simpleData: { enable: true } }, view:{ showLine: false // showIcon: false }, check: { enable: true, chkboxType: { "Y": "ps", "N": "ps" } } }; var zNodes =data; zTreeObj=$.fn.zTree.init($("#test1"), setting, zNodes); } }) }
role authorization
Permission record add backend implementation
- RoleService.java
public void addGrant(Integer[] mids, Integer roleId) { /** * Core table - t_permission t_role (verify that the role exists) * If the role has original permissions, delete the original permissions of the role * Then add new permissions for roles and batch add permissions records to t_permission */ Role temp =selectByPrimaryKey(roleId); AssertUtil.isTrue(null==roleId||null==temp,"The role to be authorized does not exist!"); int count = permissionMapper.countPermissionByRoleId(roleId); if(count>0){ AssertUtil.isTrue(permissionMapper.deletePermissionsByRoleId(roleId)<count,"Permission assignment failed!"); } if(null !=mids && mids.length>0){ List<Permission> permissions=new ArrayList<Permission>(); for (Integer mid : mids) { Permission permission=new Permission(); permission.setCreateDate(new Date()); permission.setUpdateDate(new Date()); permission.setModuleId(mid); permission.setRoleId(roleId); permission.setAclValue(moduleMapper.selectByPrimaryKey(mid).getOptValue()); permissions.add(permission); } permissionMapper.insertBatch(permissions); } }
- RoleController.java
@RequestMapping("addGrant") @ResponseBody public ResultInfo addGrant(Integer[] mids,Integer roleId){ roleService.addGrant(mids,roleId); return success("Permission added successfully"); }
Add the front-end core js to the permission record
Modify grant.js file to add ztree checkbox click callback onCheck event.
var zTreeObj; $(function () { loadModuleInfo(); }); function loadModuleInfo() { $.ajax({ type:"post", url:ctx+"/module/queryAllModules", dataType:"json", success:function (data) { // Parameter configuration of zTree, please refer to the API documentation for in-depth use (detailed explanation of setting configuration) var setting = { data: { simpleData: { enable: true } }, view:{ showLine: false // showIcon: false }, check: { enable: true, chkboxType: { "Y": "ps", "N": "ps" } }, callback: { onCheck: zTreeOnCheck } }; var zNodes =data; zTreeObj=$.fn.zTree.init($("#test1"), setting, zNodes); } }) } function zTreeOnCheck(event, treeId, treeNode) { var nodes= zTreeObj.getCheckedNodes(true); var roleId=$("#roleId").val(); var mids="mids="; for(var i=0;i<nodes.length;i++){ if(i<nodes.length-1){ mids=mids+nodes[i].id+"&mids="; }else{ mids=mids+nodes[i].id; } } $.ajax({ type:"post", url:ctx+"/role/addGrant", data:mids+"&roleId="+roleId, dataType:"json", success:function (data) { console.log(data); } }) }
The role has been added permission record echo
Here to realize the function of displaying the original permissions when viewing or authorizing the added role record permissions again, check whether the ztree checkbox selects the attribute configuration reference here.
Resource query backend method implementation
- ModuleService.java
public List<TreeDto> queryAllModules02(Integer roleId) { List<TreeDto> treeDtos=moduleMapper.queryAllModules(); // Query the menu id list<integer> List<Integer> roleHasMids=permissionMapper.queryRoleHasAllModuleIdsByRoleId(roleId); if(null !=roleHasMids && roleHasMids.size()>0){ treeDtos.forEach(treeDto -> { if(roleHasMids.contains(treeDto.getId())){ // Indicates that the current role is assigned to this menu treeDto.setChecked(true); } }); } return treeDtos; }
- role has permission sql query
<select id="queryRoleHasAllModuleIdsByRoleId" parameterType="int" resultType="java.lang.Integer"> select module_id from t_permission where role_id=#{roleId} </select>
- ModuleController.java
@RequestMapping("queryAllModules") @ResponseBody public List<TreeDto> queryAllModules(Integer roleId){ return moduleService.queryAllModules02(roleId); }
Permission echo front-end js
Here, when modifying grant.js to query resources, pass in the id of the currently selected role
function loadModuleInfo() { $.ajax({ type:"post", url:ctx+"/module/queryAllModules", data:{ roleId:$("#roleId").val() }, dataType:"json", success:function (data) { // Parameter configuration of zTree, please refer to the API documentation for in-depth use (detailed explanation of setting configuration) var setting = { data: { simpleData: { enable: true } }, view:{ showLine: false // showIcon: false }, check: { enable: true, chkboxType: { "Y": "ps", "N": "ps" } }, callback: { onCheck: zTreeOnCheck } }; var zNodes =data; zTreeObj=$.fn.zTree.init($("#test1"), setting, zNodes); } }) }
role authentication
After completing the function of adding role permissions, the next step is to authenticate the resources operated by the role. There are two pieces of authentication here:
- Menu level display controls
- Backend method access control
Menu level access control implementation
The system dynamically controls and displays the menu operated by the logged-in user according to the different roles played by the logged-in user. The control displayed here uses freemarker Instruction + built-in function implementation, instruction and built-in function operation reference here.
Login user role has permission query implementation
- IndexController.java
/** * Backend management main page * @return */ @RequestMapping("main") public String main(HttpServletRequest request){ Integer userId = LoginUserUtil.releaseUserIdFromCookie(request); request.setAttribute("user",userService.selectByPrimaryKey(userId)); List<String> permissions=permissionService.queryUserHasRolesHasPermissions(userId); request.getSession().setAttribute("permissions",permissions); return "main"; }
- PermissionService.java
@Service public class PermissionService extends BaseService<Permission,Integer> { @Autowired private PermissionMapper permissionMapper; public List<String> queryUserHasRolesHasPermissions(Integer userId) { return permissionMapper.queryUserHasRolesHasPermissions(userId); } }
- PermissionMapper.java & PermissionMapper.xml
public interface PermissionMapper extends BaseMapper<Permission,Integer> { List<String> queryUserHasRolesHasPermissions(Integer userId); }
<select id="queryUserHasRolesHasPermissions" parameterType="int" resultType="java.lang.String"> select distinct p.acl_value from t_user_role ur left join t_permission p on ur.role_id = p.role_id where ur.user_id=#{userId} </select>
System main page menu display command control
Only some of the menu controls are shown here.
<#if permissions?seq_contains("60")> <li class="layui-nav-item"> <a href="javascript:;" class="layui-menu-tips"><i class="fa fa-gears"></i><span class="layui-left-nav"> system settings</span> <span class="layui-nav-more"></span></a> <dl class="layui-nav-child"> <#if permissions?seq_contains("6010")> <dd> <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-11" data-tab="user/index" target="_self"><i class="fa fa-user"></i><span class="layui-left-nav"> User Management</span></a> </dd> </#if> <#if permissions?seq_contains("6020")> <dd class=""> <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-12" data-tab="role/index" target="_self"><i class="fa fa-tachometer"></i><span class="layui-left-nav"> role management</span></a> </dd> </#if> <#if permissions?seq_contains("6030")> <dd class=""> <a href="javascript:;" class="layui-menu-tips" data-type="tabAdd" data-tab-mpi="m-p-i-13" data-tab="module/index" target="_self"><i class="fa fa-tachometer"></i><span class="layui-left-nav"> menu management</span></a> </dd> </#if> </dl> </li> </#if>
Backend method level access control
Accomplish provides menu level display control, but eventually the client may enter the resource address through the browser to access the back-end resources across the ui interface, so the next step is to add the control method level resource access control operation. Here, aop+ custom annotation accomplish is used
Custom annotation @RequirePermission
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequirePermission { String code() default ""; }
Using annotations at the method level
@RequestMapping("list") @ResponseBody @RequirePermission(code = "101001") public Map<String,Object> querySaleChancesByParams(Integer flag,HttpServletRequest request,SaleChanceQuery saleChanceQuery){ if(null !=flag &&flag==1){ // Query marketing records assigned to the currently logged in user saleChanceQuery.setAggsinMan(LoginUserUtil.releaseUserIdFromCookie(request)); } return saleChanceService.queryByParamsForTable(saleChanceQuery); }
Define the method of aop aspect class to intercept the specified annotation annotation
@Component @Aspect public class PermissionProxy { @Autowired private HttpSession session; @Around(value = "@annotation(com.xxx.sys.annotaions.RequirePermission)") public Object around(ProceedingJoinPoint pjp) throws Throwable { List<String> permissions = (List<String>) session.getAttribute("permissions"); if(null == permissions || permissions.size()==0){ throw new NoPermissionException(); } Object result =null; MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); RequirePermission requirePermission = methodSignature.getMethod().getDeclaredAnnotation(RequirePermission.class); if(!(permissions.contains(requirePermission.code()))){ throw new NoPermissionException(); } result= pjp.proceed(); return result; } }
extension ~ custom annotation instance
Starting from JDK5, Java has added support for metadata, that is, annotations. Annotations are different from annotations. Annotations can be understood as special tags in the code. These tags can be read during compilation, class loading, runtime, and perform the corresponding processing By annotating developers can embed supplementary information in the source code without changing the original code and logic. Let's take a look at how to customize annotations.
Create custom annotation class
package com.lebyte.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /* @Target,@Retention,@Inherited,@Documented * These four are meta-annotations that annotate annotations, responsible for the attributes of custom annotations */ @Target({ElementType.TYPE,ElementType.METHOD}) //Represents the role object of the annotation, ElementType.TYPE represents the class, and ElementType.METHOD represents the method @Retention(RetentionPolicy.RUNTIME) //Represents the retention mechanism of annotations, RetentionPolicy.RUNTIME represents runtime annotations @Inherited //Indicates that the annotation is inheritable @Documented //Indicates that the annotation can generate documentation public @interface Design { String author(); //Annotation member, if the annotation has only one member, the member name must be value(), and the member type can only be the original type int data() default 0; //Annotation member, the default value is 0 }
use annotations
package com.lebyte; import com.lebyte.annotations.Design; @Design(author="lebyte",data=100) //Using custom annotations, members with default values do not need to be assigned, and the rest of the members must be assigned public class Person { @Design(author="lebyte",data=90) public void live(){ } }
Parse annotations
package com.lebyte; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import com.lebyte.annotations.Design; public class Main { public static void main(String[] args) throws ClassNotFoundException { Class c=Class.forName("com.lebyte.Person"); //Loading classes using class loader //1. Find the annotation on the class if(c.isAnnotationPresent(Design.class)){ //Determine if the class is annotated with the specified annotation Design d=(Design) c.getAnnotation(Design.class); //Get the specified annotation instance on the class System.out.println(d.data()); } //2. Find the annotations on the method Method[] ms=c.getMethods(); for(Method m:ms){ if(m.isAnnotationPresent(Design.class)){ //Determines whether the method is annotated with the specified annotation Design d=m.getAnnotation(Design.class); //Get the specified annotation instance on the class System.out.println(d.data()); } } //3. Another method for(Method m:ms){ Annotation[] as=m.getAnnotations(); //Get the annotation collection on the class for(Annotation a:as){ if(a instanceof Design){ //Determine the specified annotation Design d=(Design) a; System.out.println(d.data()); } } } } }