今日目标 : 完成登录页面和角色角色添加
[toc]
# 一.登录
## 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次,盐值:xxxxx)
#### a.MD5Util```
public class MD5Util {
public static final String SALT = "xxxxx";
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(编码方式与次数)
```
<!-- 被引用的realm(一定会写一个自定义realm) -->
<bean id="jpaRealm" class="cn.itsource.aisell.shiro.JpaRealm">
<!-- 为这个realm设置相应的匹配器 -->
<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
>设置多对多的关系(set/List都可以)
```
//Employee
@ManyToMany
@JoinTable(name = "employee_role",
joinColumns = @JoinColumn(name="employee_id"),
inverseJoinColumns = @JoinColumn(name="role_id"))
private Set<Role> roles = new HashSet<>();
//Role
@ManyToMany
@JoinTable(name="role_permission",
joinColumns = @JoinColumn(name="role_id"),
inverseJoinColumns = @JoinColumn(name="permission_id"))
private List<Permission> 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
```
<!-- 一个弹出框,里面要装一个form表单 -->
<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">
<!-- 修改的话,这个id是必需的 -->
<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;i<allRows.length;i++){
var 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;
}
@ModelAttribute("editRole")
public Role beforeEdit(Long id,String cmd){
if(id!=null && "_update".equals(cmd)){
//修改才执行这个代码
Role dbRole = roleService.findOne(id);
//解决n-to-n的问题,把关联对象设置为null
dbRole.getPermissions().clear();
return dbRole;
}
return null;
}
```