文章目录
1.添加时密码加密
当用户添加密码时,在service层对密码进行加密,然后添加到数据库
@Service
public class EmployeeServiceImpl extends BaseServiceImpl<Employee,Long> implements IEmployeeService{
@Override
public void save(Employee employee) {
//判断只有添加的时候才对密码进行加密
if(employee.getId() == null){
//对添加的密码进行加密
employee.setPassword(MD5Utils.createPwd(employee.getPassword()));
}
super.save(employee);
}
}
2.登录功能
①准备登录页面
将login页面放到views文件夹下(通过controller层访问)
shiroFilter过滤器中,配置没有登录前访问的路径/login
applicationContext-shiro.xml
<!-- 如果没有登录成功,就会进入这个页面 -->
<property name="loginUrl" value="/login"/>
前台登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>源码智销系统</title>
<%--引入头部文件--%>
<%@include file="/WEB-INF/views/head.jsp"%>
</head>
<body>
<div align="center" style="margin-top: 100px;">
<div class="easyui-panel" title="智销系统用户登陆" style="width: 350px; height: 240px;">
<form id="loginForm" class="easyui-form" method="post">
<table align="center" style="margin-top: 15px;">
<tr height="20">
<td>用户名:</td>
</tr>
<tr height="10">
<td><input name="username" class="easyui-validatebox" required="true" value="admin" /></td>
</tr>
<tr height="20">
<td>密 码:</td>
</tr>
<tr height="10">
<td><input name="password" type="password" class="easyui-validatebox" required="true" value="0" /></td>
</tr>
<tr height="40">
<td align="center"><a href="javascript:;" class="easyui-linkbutton">登录</a>
<a href="javascript:;" class="easyui-linkbutton" onclick="resetForm();">重置</a></td>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
静态资源放行
但是页面格式图片等静态资源无法访问(css,js,images…)
需要对静态资源进行放行
厂中配置放行的资源路径
public class FilterChainDefinitionMapFactory {
/**
* 这个方法就会返回咱们的权限数据(它是有顺序)
*/
public Map<String,String> creatMap(){
//注意:这里的map是有顺序的
Map<String,String> map = new LinkedHashMap<>();
map.put("/login","anon");
/*
* 静态资源放行
* */
map.put("/*.css","anon");
map.put("/*.js","anon");
map.put("/easyui/**","anon");
map.put("/css/**","anon");
map.put("/images/**","anon");
map.put("/js/**","anon");
//map.put("/json/**","anon");
map.put("/s/permission.jsp","perms[employee:save]");
map.put("/**","authc");
return map;
}
}
②完成前台登录代码
前台登录使用ajax请求的方式实现
- 为登录链接添加,点击事件
- 完成前台登录代码
- 登录成功跳转页面:location.href = “/main”;
<script>
function submitForm() {
$('#loginForm').form('submit', {
url:"/login",
onSubmit: function(){
return $(this).form('validate');
},
success:function(data){
//将返回的json字符串转换为json对象
var result = JSON.parse(data);
//判断登录是否成功
if(result.success){
//登录成功跳转到main首页
location.href = "/main";
}else{
//提示登录错误
$.messager.alert('提示','登录失败!!!<br>'+result.msg);
}
}
});
}
</script>
<a href="javascript:;" class="easyui-linkbutton" onclick="submitForm();">登录</a>
③完成登录的controller层(登录验证)
准备两个/login映射路径(RESTFUL风格)
RESTFUL风格
- 相同的路径请求方式不同,可能访问的资源也不一样
- 登录验证后,因为是ajax,所以需要返回json格式(JpaResult结果对象)
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login1(String username,String password){
//进入登录界面
return "login";
}
@RequestMapping(value = "/login",method = RequestMethod.POST)
@ResponseBody
public JpaResult login(String username,String password){
System.out.println(username+"...."+password);
//获取当前用户
Subject subject = SecurityUtils.getSubject();
try {
//将用户名和密码封装进令牌中
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//登录
subject.login(token);
} catch (UnknownAccountException e) {
//UnknownAccountException(不知道用户异常)
System.out.println("用户名错误!!!");
e.printStackTrace();
return new JpaResult(false,"用户名错误!!!");
}catch (IncorrectCredentialsException e){
//IncorrectCredentialsException(不正确凭证(密码错误)异常)
System.out.println("密码错误!!!");
e.printStackTrace();
return new JpaResult(false,"用户名或者密码错误!!!");
}catch (AuthenticationException e){
System.out.println("sorry!!!");
e.printStackTrace();
return new JpaResult(false,"神秘错误!!!");
}
//登录成功返回一个结果
return new JpaResult();
}
④真实数据(登录)
- Realm类中,查询数据库真实数据验证登录是否成功
- service层准备**Employee findByUsername(String username);通过用户名查询用户对象**
IEmployeeService接口
public interface IEmployeeService extends IBaseService<Employee,Long>{
//根据用户名查询
Employee findByUsername(String username);
}
EmployeeServiceImpl实现类 重写findByUsername(String username)方法
@Service
public class EmployeeServiceImpl extends BaseServiceImpl<Employee,Long> implements IEmployeeService{
@Autowired
private EmployeeRepository employeeRepository;
@Override
public void save(Employee employee) {
//判断只有添加的时候才对密码进行加密
if(employee.getId() == null){
//对添加的密码进行加密
employee.setPassword(MD5Utils.createPwd(employee.getPassword()));
}
super.save(employee);
}
@Override
public Employee findByUsername(String username) {
//通过用户名查询用户
return employeeRepository.findByUsername(username);
}
}
AisellRealm类查询真实用户
public class AisellRealm extends AuthorizingRealm {
@Autowired
private IEmployeeService employeeService;
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//拿到用户名密码令牌(强转)
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//获取用户名(传过来的)
String username = token.getUsername();
//根据用户名到数据库,查询对应的用户----------------------
Employee loginUser = employeeService.findByUsername(username);
//如果用户为null,表示用户不存在
if (loginUser == null){
return null;
}
//判断密码是否正确
//shiro做准备了一个工具直接让我们把数据变成 ByteSource格式
ByteSource salt = ByteSource.Util.bytes(MD5Utils.SALT);
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(loginUser,loginUser.getPassword(),salt,getRealmName());
return authenticationInfo;
}
private String getRealmName(){
return "myRealm";
}
}
1.细节-回车登录
//监听回车登录事件 keyup释放键盘
$(document.documentElement).on("keyup",function (event) {
var keyCode = event.keyCode;
if(keyCode == 13){//捕获回车
submitForm();//提交表单
}
});
2.细节-过期登录(窗口嵌套错误)
模拟:用户过期后重新访问时,在嵌套的窗口中显示了登录页面
如果当前页面(windw)不是顶层页面(top),当前页面改为顶层
//如果当前页面(windw)不是顶层页面(top)
if(top != window){
//跳转到顶层页面
top.location.href = window.location.href;
}
3.细节-注销
main.jsp页面
引入shiro标签库
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
通过实体 取实体的属性username的值
<%--北--%>
<div data-options="region:'north'" style="height:120px;">
<h1 style="margin-top: 38.767px;margin-right: 0px;margin-left: 47px;">智销系统</h1>
<div style="text-align: right;padding-right: 20px" >
欢迎您,亲爱的:<shiro:principal property="username"/> <a href="/logout">注销</a>
</div>
</div>
3.完成角色与权限的增删改查
与员工类似,首页有不同
/role/index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
<%--引入头部文件--%>
<%@include file="/WEB-INF/views/head.jsp"%>
<%--引入当前模块js文件--%>
<script type="text/javascript" src="/js/model/role.js"></script>
</head>
<body>
<div id="tb" style="padding:5px;height:auto">
<div style="margin-bottom:5px">
<a href="#" data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
<a href="#" data-method="update" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
<a href="#" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
<%--查询表单--%>
<form id="searchForm" method="post">
角色名: <input name="name" class="easyui-textbox" style="width:80px">
<a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查询</a>
</form>
</div>
<%--数据表格(展示当前模块数据)--%>
<table id="datagrid" class="easyui-datagrid" style="width:400px;height:250px"
data-options="url:'/role/page',fitColumns:true,singleSelect:true,fit:true,pagination:true,toolbar:'#tb'">
<thead>
<tr>
<th data-options="field:'sn',width:20">编码</th>
<th data-options="field:'name',width:20">名称</th>
<th data-options="field:'permissions',width:100,formatter:permsFormat">权限</th>
</tr>
</thead>
</table>
<%--添加或保存对话框--%>
<div id="dialog" class="easyui-dialog" title="角色管理" style="width:700px"
data-options="iconCls:'icon-save',resizable:true,modal:true,closed:true,buttons:'#buttons'">
<%--信息表单--%>
<form id="editForm" method="post">
<table cellpadding="5">
<input id="roleId" type="hidden" name="id"/>
<tr>
<td>
编码:<input class="easyui-textbox" type="text" name="sn" data-options="required:true"/>
角色名称:<input class="easyui-textbox" type="text" name="name" data-options="required:true"/>
</td>
</tr>
</table>
<div id="cc" class="easyui-layout" style="width:100%;height:400px;">
<div data-options="region:'west'" style="width:50%;">
<%--角色的权限--%>
<table id="rolePermsGird">
<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="allPermsGird">
<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>
<%--保存/取消按钮--%>
<div id="buttons">
<a href="#" data-method="save" class="easyui-linkbutton c3">保存</a>
<a href="#" data-method="close" class="easyui-linkbutton c4">关闭</a>
</div>
</body>
</html>
/permission/index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
<%--引入头部文件--%>
<%@include file="/WEB-INF/views/head.jsp"%>
<%--引入当前模块js文件--%>
<script type="text/javascript" src="/js/model/permission.js"></script>
</head>
<body>
<%--顶部工具栏--%>
<div id="tb" style="padding:5px;height:auto">
<div style="margin-bottom:5px">
<a href="#" data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
<a href="#" data-method="update" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
<a href="#" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
<%--查询表单--%>
<form id="searchForm" method="post">
权限名: <input name="name" class="easyui-textbox" style="width:80px">
<a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查询</a>
</form>
</div>
<%--数据表格(展示当前模块数据)--%>
<table id="datagrid" class="easyui-datagrid" style="width:400px;height:250px"
data-options="url:'/permission/page',fitColumns:true,singleSelect:true,fit:true,pagination:true,toolbar:'#tb'">
<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>
<th data-options="field:'descs',width:100">描述</th>
</tr>
</thead>
</table>
<%--添加或保存对话框--%>
<div id="dialog" class="easyui-dialog" title="权限管理"
data-options="iconCls:'icon-save',resizable:true,modal:true,closed:true,buttons:'#buttons'">
<%--信息表单--%>
<form id="editForm" method="post">
<table cellpadding="5">
<input id="permissionId" type="hidden" name="id"/>
<tr>
<td>权限名称:</td>
<td><input class="easyui-textbox" type="text" name="name" data-options="required:true"></input></td>
</tr>
<tr>
<td>编码:</td>
<td><input class="easyui-textbox" type="text" name="sn" data-options="required:true"></input></td>
</tr>
<tr>
<td>资源路径:</td>
<td><input class="easyui-textbox" type="text" name="url" data-options="required:true"></input></td>
</tr>
<tr>
<td>权限描述:</td>
<td><input class="easyui-textbox" type="text" name="descs" data-options="required:true"></input></td>
</tr>
</table>
</form>
</div>
<%--保存/取消按钮--%>
<div id="buttons">
<a href="#" data-method="save" class="easyui-linkbutton c3">保存</a>
<a href="#" data-method="close" class="easyui-linkbutton c4">关闭</a>
</div>
</body>
</html>
4.员工-角色-权限(多对多配置)
Employee(员工)
员工与角色是多对多的关系
@Entity
@Table(name = "employee")
public class Employee extends BaseDomain{
private String username;
private String password;
private String email;
private Integer age;
private String headImage;
//多个员工对应一个部门
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;
/*
* 员工与角色是多对多的关系
* JoinTable:关连表
* joinColumns:当前这个表对应中间表的外键
* inverseJoinColumns:关连的对应的表的外键
* */
@ManyToMany
@JoinTable(name = "employee_role",
joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private List<Role> roles = new ArrayList<>();
//getter,setter,toString...
}
Role(角色)
角色与权限是多对多的关系
@Entity
@Table(name = "role")
public class Role extends BaseDomain{
//角色名称
private String name;
//角色编码
private String sn;
/*
* 角色与权限是多对多关系
* */
@ManyToMany
@JoinTable(name = "role_permission",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "permission_id")
)
private List<Permission> permissions = new ArrayList<>();
//getter,setter,toString...
}
角色与资源是(各种关系都有)
本项目是采用一对一,权限与资源一张表
5.角色
①角色查询(权限显示)
为权限的显示进行格式化
<%--数据表格(展示当前模块数据)--%>
<table id="datagrid" class="easyui-datagrid" style="width:400px;height:250px"
data-options="url:'/role/page',fitColumns:true,singleSelect:true,fit:true,pagination:true,toolbar:'#tb'">
<thead>
<tr>
<th data-options="field:'sn',width:20">编码</th>
<th data-options="field:'name',width:20">名称</th>
<th data-options="field:'permissions',width:100,formatter:permsFormat">权限</th>
</tr>
</thead>
</table>
js代码
遍历数组,拼接所有权限名
function permsFormat(v) {
//这里v是一个数组,里面装的permission对象
/*
* for(var p in v){} 取的数组角标
* for(var p of v){} 取的数组里的元素
* */
var ps = "";
//遍历数组,拼接
for(var p of v){
ps += p.name+" ";
}
return ps;
}
②添加角色
角色添加-布局
使用js创建两个DataGrid(当前角色权限与所有权限)
js代码
//创建两个datagird
//当前角色拥有权限
rolePermsGird.datagrid({
fitColumns:true,
singleSelect:true,
fit:true,
onDblClickRow:itsource.removePerms
});
//所有的权限
allPermsGird.datagrid({
url:'/permission/list',
fitColumns:true,
singleSelect:true,
fit:true,
onDblClickRow:itsource.addPerms
});
/role/index.jsp
<%--添加或保存对话框--%>
<div id="dialog" class="easyui-dialog" title="角色管理" style="width:700px"
data-options="iconCls:'icon-save',resizable:true,modal:true,closed:true,buttons:'#buttons'">
<%--信息表单--%>
<form id="editForm" method="post">
<table cellpadding="5">
<input id="roleId" type="hidden" name="id"/>
<tr>
<td>
编码:<input class="easyui-textbox" type="text" name="sn" data-options="required:true"/>
角色名称:<input class="easyui-textbox" type="text" name="name" data-options="required:true"/>
</td>
</tr>
</table>
<div id="cc" class="easyui-layout" style="width:100%;height:400px;">
<div data-options="region:'west'" style="width:50%;">
<%--角色的权限--%>
<table id="rolePermsGird">
<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="allPermsGird">
<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>
角色添加-追加权限
- 需要判断追加是否重复(遍历所有行判断,getRows)
- rolePermsGird.datagrid(“appendRow”,row);添加行
itsource = {
addPerms(index,row){
//获取当前角色所有行
var rows =rolePermsGird.datagrid("getRows");
//遍历所有行
for(var r of rows){
if(r.id == row.id){
$.messager.show({
title:'错误',
msg:'你已经添加过了,不能重复添加!!!',
showType:'slide',
style:{
right:'',
top:document.body.scrollTop+document.documentElement.scrollTop,
bottom:''
}
});
return;
}
}
rolePermsGird.datagrid("appendRow",row);
},
...
}
//所有的权限
allPermsGird.datagrid({
url:'/permission/list',
fitColumns:true,
singleSelect:true,
fit:true,
onDblClickRow:itsource.addPerms
});
角色添加-双击移除权限
itsource = {
removePerms(index){
rolePermsGird.datagrid("deleteRow",index);
}
}
//创建两个datagird
//当前角色拥有权限
rolePermsGird.datagrid({
fitColumns:true,
singleSelect:true,
fit:true,
onDblClickRow:itsource.removePerms
});
角色添加=清空DataGrid
- 添加前清空角色权限的DataGrid
- loadData方法,加载本地数据覆盖之前数据(加载空数组,实现清空)
itsource = {
add(){
...
//清理rolePermsGird中的所有行(利用空数组)
rolePermsGird.datagrid("loadData",[]);
}
}
角色添加-权限传参
示例图:
- 前台权限信息是一个数组装的,直接传到后台,后台处理很麻烦
- DataGrid不是表单元素,不能表单提交,需要以表单额外参数方式提交
- SpringMVC,有特殊的传参方式:
param[`permissions[${i}].id`] = p.id;
save(){
//默认添加路径
var url = "/role/save";
//获取id
var roleId = $("#roleId").val();
if(roleId){
url = "/role/update?_cmd=update";
}
editForm.form('submit', {
url:url,
onSubmit: function(param){
//表单提交额外参数,param是空的,(给它加啥,他就会往后台传啥)
//获取当前角色的所有行
var rows = rolePermsGird.datagrid("getRows");
//遍历所有行
for(var i = 0;i<rows.length;i++){
var p = rows[i];
param[`permissions[${i}].id`] = p.id;
}
//提交前执行的操作 一般用于验证
return $(this).form("validate");
},
success:function(data){
//从后台获取到json格式的字符串{"success":true,"msg":null}
//将json格式字符串转换为json对象
var result = JSON.parse(data);
//如果添加成功
if(result.success){
//重新加载数据
datagrid.datagrid("reload");
}else{
//添加失败返回失败信息
$.messager.alert('提示',`添加失败了,原因是:${result.msg}`,'error');
//$.messager.alert('提示','添加失败了,原因是:'+result.msg,'error');
}
//关闭对话框
itsource.close();
}
});
}
角色修改-回显权限
- 注意不能直接获取row.permissions进行回显,应该复制一份用于回显
因为直接加载row的话,是操作的同一个数据(row),下次回显的是上次修改过后的数据
复制数组的代码:
var perms = [...row.permissions];
分析图:
回显代码
//回显权限数据
//复制数组
var perms = [...row.permissions];
console.debug(perms)
rolePermsGird.datagrid("loadData",perms);
update(){
//修改时,需要判断是否选中一行
//获取行
var row = datagrid.datagrid("getSelected");
if(!row){
//提示选中
$.messager.alert('提示','请选中一行再删除','warning');
return;
}
//修改时,不需要修改密码,设置标识,隐藏密码项
$("*[shower]").hide();
//禁用密码,不让让传值
$("*[shower]>td>input").textbox("disable");
//点击添加,弹出对话框
dialog.window("open").window("center");
//每次打开对话框先清理表单数据
editForm.form("clear");
//回显
editForm.form("load",row);
//回显权限数据
//复制数组
var perms = [...row.permissions];
console.debug(perms)
rolePermsGird.datagrid("loadData",perms);
}
角色修改-no to no错误
- 修改了持久化对象id
- 将权限集合清空
controller层
@ModelAttribute("editRole")
public Role aaa(Long id,String _cmd){
if(id != null && "update".equals(_cmd)){//只有修改数据,才返回查找的对象
Role role = roleService.findOne(id);
//将关连对象设置为null,集合的话就清空
role.getPermissions().clear();
return role;
}
return null;
}