一.登录
1.1 准备登录页面(随便找一个即可)
位置:webapp/WEB-INF/views/login.jsp
js代码部分
//登录功能
function submitForm(){ $('#loginForm').form('submit', { url:"/login", onSubmit: function(){ return $(this).form('validate'); }, success:function(data){ var result = JSON.parse(data); if(result.success){ //成功后跳转到主页面 window.location.href = "/main"; }else{ $.messager.alert("提示",result.msg,"error"); } } }); }
1.2 LoginController功能
1.2.1 两个login方法
一个用于跳转(/login,get),一个用于登录(/login,post)
@RequestMapping(value="/login",method = RequestMethod.GET)
public String index(){
return "login"; } /** * 是一个Ajax的登录请求,它会返回{success:true/false,msg:xxx} * @return */ @RequestMapping(value="/login",method = RequestMethod.POST) @ResponseBody public JsonResult login(String username, String password){ ... }
1.2.2完成登录的功能
@RequestMapping(value="/login",method = RequestMethod.POST)
@ResponseBody
public JsonResult login(String username, String password){
//1.拿到当前用户 Subject currentUser = SecurityUtils.getSubject(); //2.如果没有登录,进行登录 if(!currentUser.isAuthenticated()){ //3.准备令牌 UsernamePasswordToken token = new UsernamePasswordToken(username, password); //4.实现登录 try { currentUser.login(token); } catch (UnknownAccountException e) { e.printStackTrace(); return new JsonResult(false, "用户名不存在!"); } catch (IncorrectCredentialsException e) { e.printStackTrace(); return new JsonResult(false, "账号或密码错误!"); } catch (AuthenticationException e) { e.printStackTrace(); // System.out.println("就是登录不了(请联系程序员...)"); return new JsonResult(false, "网络出错(联系管理员)!"); } } //登录成功成功令牌 return new JsonResult(); }
1.3 登录细节
1.3.1 数据库的密码设置
要有一套自己的密码规则(md5,10次,盐值:itsource)
a.MD5Util
public class MD5Util {
public static final String SALT = "itsource"; public static final Integer HASHITERATIONS = 10; //密码加密 public static String changePwd(String password){ SimpleHash hash = new SimpleHash("MD5",password,SALT,HASHITERATIONS); return hash.toHex(); } }
b.添加用户密码加密
controller或者service中都可以进行[我们选择service]
@Override
public void save(Employee employee) { if(employee.getId()==null){ //添加功能就进行密码修改 employee.setPassword(MD5Util.changePwd(employee.getPassword())); } employeeRepository.save(employee); }
c.加密的判断必需和规则一致
applicationContext-shiro.xml(编码方式与次数)
<bean id="jpaRealm" class="cn.itsource.aisell.shiro.JpaRealm"> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="md5"/> <property name="hashIterations" value="10" /> bean> property> bean>
JpaRealm(加盐一致)
//身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.拿用户名与密码 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; String username = token.getUsername(); //2.根据用户名拿到相应的对象 Employee loginUser = employeeService.findByUsername(username); if(loginUser==null){ return null; //如果用户用空代表用户名不存在 } String password = loginUser.getPassword(); //返回认证信息 //准备盐值 //传的第一个值就是主体(username名称做的主体) ByteSource salt = ByteSource.Util.bytes(MD5Util.SALT); SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,getName()); return authenticationInfo; }
1.3.2 其它细节
a.静态资源放行
有些地方没有登录也可以直接使用(FilterChainDefinitionMapFactory)
public Map<String,String> createFilterChainDefinitionMap(){
//注:LinkedHashMap是有序的 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/s/login.jsp", "anon"); filterChainDefinitionMap.put("/login", "anon"); //把所有静态资源进行放行 filterChainDefinitionMap.put("*.js", "anon"); filterChainDefinitionMap.put("*.css", "anon"); filterChainDefinitionMap.put("/easyui/**", "anon"); filterChainDefinitionMap.put("/images/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/s/permission.jsp", "perms[user:index]"); filterChainDefinitionMap.put("/**", "authc"); return filterChainDefinitionMap; }
b.登录过期
login.jsp
// 检查自己是否是顶级页面
if (top != window) {// 如果不是顶级 //把子页面的地址,赋值给顶级页面显示 window.top.location.href = window.location.href; }
c.回车登录
login.jsp
$(document.documentElement).on("keyup", function(event) { //console.debug(event.keyCode); var keyCode = event.keyCode; console.debug(keyCode); if (keyCode === 13) { // 捕获回车 submitForm(); // 提交表单 } });
d.展示用户名与注销
main.jsp
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> ... <div style="text-align: right;margin-right: 20px;"> 欢迎您,亲爱的用户:<shiro:principal /> <a href="/logout">注销a> div>
LoginController
@RequestMapping("/logout")
public String logout(){
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return "redirect:/login"; }
二.角色管理
先使用代码生成器生成Role,Permission 设置多对多的关系
//Employee
@ManyToMany
@JoinTable(name = "employee_role",
joinColumns = @JoinColumn(name="employee_id"), inverseJoinColumns = @JoinColumn(name="role_id")) private Set roles = new HashSet<>();
//Role
@ManyToMany
@JoinTable(name="role_permission",
joinColumns = @JoinColumn(name="role_id"), inverseJoinColumns = @JoinColumn(name="permission_id")) private List permissions = new ArrayList<>();
2.1 角色中(权限的展示)
role.jsp
<table id="roleGrid" class="easyui-datagrid" fit="true" ... "> <thead> <tr> ... <th data-options="field:'permissions',width:100,formatter:formatPerms">权限th> tr> thead> table>
role.js
//返回权限展示的方法
//v:当前数据 r:当前行数据 i:行索引 function formatPerms(v,r,i){ var permsName = ""; for(let o of v){ permsName += o.name +" "; } return permsName; }
2.1 添加权限
2.1.1 form弹出布局
左(当前权限)右(所有权限)都有一个grid
<div id="editDlg" class="easyui-dialog" title="功能操作" data-options="iconCls:'icon-save',closed:true,modal:true" style="padding:10px;width: 850px;"> <form id="editForm" method="post"> <input id="roleId" name="id" type="hidden"/> <table cellpadding="5"> <tr> <td> 名称:<input class="easyui-validatebox" type="" name="name" data-options="required:true">input> 编码:<input class="easyui-validatebox" type="text" name="sn" data-options="required:true">input> td> tr> table> <div class="easyui-layout" style="width:100%;height:400px;"> <div data-options="region:'west'" style="width:50%;"> <table id="rolePermissionGrid"> <thead> <tr> <th data-options="field:'name',width:100">名称th> <th data-options="field:'sn',width:100">编码th> <th data-options="field:'url',width:100">资源路径th> tr> thead> table> div> <div data-options="region:'center'"> <table id="allPermissionGrid"> <thead> <tr> <th data-options="field:'name',width:100">名称th> <th data-options="field:'sn',width:100">编码th> <th data-options="field:'url',width:100">资源路径th> tr> thead> table> div> div> form> <div style="text-align:center;padding:5px"> <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交a> <a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#editDlg').dialog('close')">关闭a> div> div>
2.1.2 为角色添加/删除权限
...
var rolePermissionGrid = $("#rolePermissionGrid");
var allPermissionGrid = $("#allPermissionGrid");
...
itsource = {
add(){
...
//清空grid中的数据 //loadData:加载本地数据,旧的行将被移除 rolePermissionGrid.datagrid("loadData",[]); ... }, edit(){ ... if(row){ ... //拷备对应的权限数组 var copyPerms = [...row.permissions]; //解决Grid加显问题 rolePermissionGrid.datagrid("loadData",copyPerms); }else { ... }, //通过javascript进行保存 save(){ ... editForm.form('submit', { //form提交的路径 url:url, //提交之前你要做什么事件 onSubmit: function(param){ //添加一些提交的额外参数 //1.拿到grid中所有的值 var allRows = rolePermissionGrid.datagrid("getRows"); //2.循环把值放进去 for(var i=0;ivar row = allRows[i]; param[`permissions[${i}].id`] = row.id; } return $(this).form('validate'); }, ... }); }, ... //添加一个权限 addPerms(index, row){ //先拿到角色的所有权限 var allRows = rolePermissionGrid.datagrid("getRows"); //遍历进行比较 for(let o of allRows){ //如果两个权限相等,就什么都不做了 if(o.id == row.id){ $.messager.show({ title:'注意事项', msg:'这个权限已经存在,无需再进行添加!', timeout:2000, showType:'slide' }); return; } } rolePermissionGrid.datagrid("appendRow",row); }, //删除对应权限 delPerms(index,row){ rolePermissionGrid.datagrid("deleteRow",index); } }; //创建当前角色对应的权限(grid控件) rolePermissionGrid.datagrid({ fit:true, fitColumns:true, singleSelect:true, border:false, onDblClickRow:itsource.delPerms }) //创建拿到所有的权限的grid控件 allPermissionGrid.datagrid({ fit:true, url:'/permission/page', fitColumns:true, singleSelect:true, pagination:true, border:false, onDblClickRow:itsource.addPerms })
2.1.3 只增不减的问题
修改的时候只能添加不能减少
@ModelAttribute("editRole")
public Role beforeEdit(Long id,String cmd){
//修改的时候才查询(只要有id会就进行一次查询,这是不对的) if(id!=null && "update".equals(cmd)) { Role role = roleService.findOne(id); //把要传过来的关联对象都清空,就可以解决n-to-n的问题 role.getPermissions().clear(); return role; } return null; }