第一章:用户认证功能完善
第一节:显示用户名功能
使用SpringSecurity框架进行操作时,SpringSecurity会产生一个上下文对象SecurityContext,该上下文对象会被自动存储到session域中,于此同时也会将该上下文对象绑定到当前线程上,通过SecurityContext可以获得认证对象Authentication,而认证对象Authentication内部封装了principal(主角)属性,该principal就是当前用户对象User,而User对象自然包含用户名等信息。
获取过程:SecurityContext—>Authentication—>User—>username
1、获得用户名方式一:
服务器端可以通过程序API的方式获得
//获取登录名的类
@Controller
public class ShowUsernameController {
//注意:需要有路径访问时才能获取到登录名
@RequestMapping("/showUsername")
public void showUsername(HttpServletRequest request){
//第1种写法
// //获取session对象
// HttpSession session = request.getSession();
// //从session域中获取登录信息
// //从session域中获取所有的属性名
// //枚举类型:遍历枚举类型
// Enumeration attributeNames = session.getAttributeNames();
// //attributeNames.hasMoreElements() 判断是否有更多的元素
// //attributeNames.nextElement() 获取枚举中下一个元素
// while(attributeNames.hasMoreElements()){
// System.out.println(attributeNames.nextElement());
// }
// //SPRING_SECURITY_CONTEXT : 存储用户登录信息的session中的名称
// Object spring_security_context = session.getAttribute("SPRING_SECURITY_CONTEXT");
System.out.println(spring_security_context);
// //获取的是SecurityContext对象,是安全框架的上下文对象
// SecurityContext securityContext = (SecurityContext) spring_security_context;
// // 获取认证信息
// Authentication authentication = securityContext.getAuthentication();
// //Principal: 翻译:重要的 -- 获取的是重要信息 -- 用户详情(UserDetails)
// Object principal = authentication.getPrincipal();
// User user = (User) principal;
// //获取用户名
// String username = user.getUsername();
// System.out.println(username);
//第2种写法
//得到spring安全框架上下文对象
SecurityContext context = SecurityContextHolder.getContext();
//获取登录信息对象
Authentication authentication = context.getAuthentication();
//获取安全框架的重要(Principal)登录对象,且强转为安全框架内置的user对象
User user = (User)authentication.getPrincipal();
//获取登录名
String username = user.getUsername();
System.out.println(username);
}
}
2、获得用户名方式三:
在页面中通过el表达式从session域中获得
${sessionScope.SPRING_SECURITY_CONTEXT.authentication.principal.username}
3、获得用户名方式四:
在页面中通过SpringSecurity的标签直接获得
<security:authentication property="principal.username" />
4、显示用户名效果
[外链图片转存失败(img-jjYkKKsG-1569005164724)(img\1.png)]
第二节:用户退出功能
在header.jsp的页面中编写超链接
<a href="${pageContext.request.contextPath}/logout"
class="btn btn-default btn-flat">注销</a>
效果图
[外链图片转存失败(img-71a6sXLb-1569005164725)(img\2.png)]
第二章:用户模块
第一节:用户列表查询功能
1、页面入口
[外链图片转存失败(img-WVtVgjCY-1569005164725)(img\3.png)]
2、编写Controller
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/findAll")
public ModelAndView findAll(){
//查询数据
List<SysUser> userList = userService.findAll();
//创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("userList",userList);
//指定页面
modelAndView.setViewName("user-list");
//返回ModelAndView对象
return modelAndView;
}
}
3、编写Service
接口
public interface UserService extends UserDetailsService{
/**
* 查询全部
* @return
*/
List<SysUser> findAll();
}
实现
@Override
public List<SysUser> findAll() {
return userDao.findAll();
}
4、编写Dao
/**
* 查询全部
* @return
*/
@Select("select * from sys_user")
List<SysUser> findAll();
第二节:添加用户功能
1、页面入口
[外链图片转存失败(img-ahQUygRE-1569005164726)(img\4.png)]
2、编写Controller
/**
* 保存用户
* @param user
* @return
*/
@RequestMapping("/save")
public String save(SysUser user){
//保存操作
userService.save(user);
//请求查询全部
return "redirect:/user/findAll";
}
3、编写Service
接口
/**
* 保存用户
* @param user
*/
void save(SysUser user);
实现
@Override
public void save(SysUser user) {
userDao.save(user);
}
4、编写Dao
/**
* 保存用户
* @param user
*/
@Insert("insert into sys_user values(user_seq.nextval,#{username},#{email},#{password},#{phoneNum},#{status})")
void save(SysUser user);
第三节:判断唯一用户名
1、页面入口
[外链图片转存失败(img-AuRMH2Ru-1569005164727)(img/15.png)]
编写页面
<%--
onchange事件, 如果修改表单的内容,且失去焦点,则触发事件
onblur事件:失去焦点就会触发事件
--%>
<input type="text" class="form-control" id="username" name="username" οnchange="isUniqueUsername(this)"
placeholder="用户名称" value="">
<script type="text/javascript">
/**
* 判断是否唯一用户名
*/
function isUniqueUsername(a){
// alert(a.value);
var username = $("#username").val();
//请求Controller判断用户名是否唯一
$.ajax({
url:"${pageContext.request.contextPath}/user/isUniqueUsername",
data:{"username":username},
type:"post",
success:function(data){
//回调函数-- data,返回流
if(data == "false"){
//如果返回false,不可以使用,注意springMvc 返回的是字符串,对比时false,要加引号
//提示用户用户名已经被占用 -- 设置文本框为红色边框 -- 边框为红色,宽度为1,样式为实线
$("#username").attr("style", "border:red 1px solid");
//保存按钮设置为不可用(失效)
//$("#savebtn").prop("disabled",true); 设置标签属性的jquery
//把按钮关闭的属性打开,意思是不可用,不可编辑
//disabled的属性为false,就是设置为可用可编辑的意思。
$("#saveBtn").prop("disabled",true);
}else{
//如果返回true,则设置保存按钮可用
//设置文本框还原状态
$("#username").attr("style", "");
//还原成原来的样式
// $("#username").addClass("form-control");
//保存按钮可用
$("#saveBtn").prop("disabled",false);
}
}
});
// $.ajax({
// url:"请求路径",
// data:"请求数据",
// dataType:"请求数据类型:json",
// type:"提交方式:get,post",
// success:function(data){
// //回调函数-- data,返回流
// }
// });
}
</script>
2、编写Controller
/**
* 判断是否唯一用户名
*
* @ResponseBody: 把结果集转成json以流的形式返回
* @param username
* @return
*/
@RequestMapping("/isUniqueUsername")
@ResponseBody
public String isUniqueUsername(String username){
//判断是否存在该用户名
boolean b = userService.isUniqueUsername(username);
return String.valueOf(b);
}
3、编写service
/**
* 判断唯一用户名
* @param username
* @return
*/
boolean isUniqueUsername(String username);
4、编写Dao
@Override
public boolean isUniqueUsername(String username) {
SysUser user = userDao.findAllUsersByUsername(username);
//如果user为null,说明可以使用此用户名,返回true
//如果user不为null,说明不可以使用此用户名,返回false
return user == null;
}
第四节:用户密码的加密操作
为了客户账户的安全性,在数据库中不能明文显示密码,所以就需要对客户端提交的密码进行加密后在存储到数据库中。原来可以使用md5、加盐加密等工具进行密码加密,目前我们使用的SpringSecurity框架本身就提供了加密工具。
public class MD5Utils { //加密工具类
@Autowired //注入加密工具类的属性
PasswordEncoder passwordEncoder;
/**
* 使用md5的算法进行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
// 如果生成数字未满32位,需要前面补0
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
public static void main(String[] args) {
System.out.println(md5("123"));
}
}
0、md5加密
@Override
public void save(SysUser user) {
//获取明文密码
String password = user.getPassword();
//对明文密码进行加密
String md5Password = MD5Utils.md5(password);
//把加密后的密码存储到user对象中
user.setPassword(md5Password);
userDao.save(user);
}
1、在spring-security.xml中配置加密类BCryptPasswordEncoder
<!--创建一个加密工具类,注意:这个类的位置,必须在spring-security.xml中的最底部。否则,有加密登录方面的问题-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
2、修改save业务方法,加盐加密的保存方法
@Autowired //注入加密工具类的属性
PasswordEncoder passwordEncoder;
public void save(SysUser user) {//
//获取明文密码
String password = user.getPassword();
//对明文密码进行加密
String securityPassword = passwordEncoder.encode(password);
//把加密后的密码存储到user对象中
user.setPassword(securityPassword);
userDao.save(user);
}
3、加密效果
[外链图片转存失败(img-iK20ZVnB-1569005164728)(img\5.png)]
4、修改登录操作
用户在登录时的密码也需要进行加密才能与数据库中的密码进行匹配,但认证时的加密操作不需要手动编写程序实现,只需要对spring-security.xml进行配置即可。
修改spring-security.xml
<security:authentication-manager>
<!--认证信息的提供者: 关联用户服务对象-提供账号和密码-->
<security:authentication-provider user-service-ref="userService">
<!--指定登录加密工具类-->
<security:password-encoder ref="passwordEncoder"></security:password-encoder>
<!--用户的服务对象-->
<!--<security:user-service>-->
<!--用户信息:是临时账号和密码-->
<!--{noop}: 不使用加密-->
<!--authorities: 指定用户的认证角色-->
<!--<security:user name="admin" password="{noop}admin" authorities="ROLE_USER"/>-->
<!--</security:user-service>-->
</security:authentication-provider>
</security:authentication-manager>
修改userServiceImpl的认证方法loadUserByUsername
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名获取用户(SysUser)对象
SysUser sysUser = userDao.findByUsername(username);
if(sysUser != null){
//创建角色的集合对象
Collection<GrantedAuthority> authorities = new ArrayList<>();
//创建临时的角色对象
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
//对象添加到集合中
authorities.add(grantedAuthority);
/**
* 参数1:用户名
* 参数2:密码 需要设置不加密"{noop}"前缀,取消则为加密
* 参数3:角色列表对象
*/
UserDetails user = new User(sysUser.getUsername(),sysUser.getPassword(), authorities);
return user;
}
return null;
}
第三章:角色模块
第一节:角色表与实体的创建
1、角色表建表语句和字段含义
create sequence role_seq;
CREATE TABLE sys_role(
id number PRIMARY KEY,
roleName VARCHAR2(50) ,
roleDesc VARCHAR2(50)
)
insert into sys_role values(role_seq.nextval, 'ADMIN','管理员');
字段含义:
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | bigint | 无意义,主键自动增长 |
2 | roleName | varchar | 角色名 |
3 | roleDesc | varchar | 角色描述 |
2、Role实体创建
package com.itheima.domain;
public class Role {
private Long id;
private String roleName;
private String roleDesc;
//此处省略getter和setter方法... ...
}
第二节:角色列表查询功能
1、页面入口
[外链图片转存失败(img-dUvFNTY7-1569005164728)(img\6.png)]
2、编写Controller
@Controller
@RequestMapping("/role")
public class RoleController {
@Autowired
RoleService roleService;
/**
* 查询全部
* @return
*/
@RequestMapping("/findAll")
public ModelAndView findAll(){
//准备数据
List<Role> roleList = roleService.findAll();
//创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("roleList",roleList);
//指定页面
modelAndView.setViewName("role-list");
//返回ModelAndView
return modelAndView;
}
}
3、编写Service
接口
public interface RoleService {
/**
* 查询全部
* @return
*/
List<Role> findAll();
}
实现
@Service
public class RoleServiceImpl implements RoleService {
@Autowired
RoleDao roleDao;
@Override
public List<Role> findAll() {
return roleDao.findAll();
}
}
4、编写Dao
public interface RoleDao {
/**
* 查询全部
* @return
*/
@Select("select * from sys_role")
List<Role> findAll();
}
第三节:添加角色功能
1、页面入口
[外链图片转存失败(img-7mV19WHt-1569005164729)(img\11.png)]
2、编写Controller
/**
* 保存角色
* @param role
* @return
*/
@RequestMapping("/save")
public String save(Role role){
//执行保存操作
roleService.save(role);
//请求查询全部
return "redirect:/role/findAll";
}
3、编写Service
接口
/**
* 保存角色
* @param role
*/
void save(Role role);
实现
@Override
public void save(Role role) {
roleDao.save(role);
}
4、编写Dao
/**
* 保存角色
* @param role
*/
@Insert("insert into sys_role values(role_seq.nextval, #{roleName},#{roleDesc})")
void save(Role role);
第四章:权限模块
第一节:权限表与实体的创建
1、权限表建表语句和字段含义
create sequence permission_seq;
CREATE TABLE sys_permission(
id number PRIMARY KEY,
permissionName VARCHAR2(50) ,
url VARCHAR2(50),
pid number
)
字段含义:
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | bigint | 无意义 |
2 | permissionName | varchar | 权限名 |
3 | url | varchar | 资源路径 |
4 | pid | bigint | 父菜单id |
2、Permission实体创建
package com.itheima.domain;
public class Permission {
private Long id;
private String permissionName;
private String url;
private Long pid;
//此处省略getter和setter方法... ...
}
第二节:权限列表查询功能
1、页面入口
[外链图片转存失败(img-mmRmQnrY-1569005164730)(img\7.png)]
2、编写Controller
@Controller
@RequestMapping("/permission")
public class PermissionController {
@Autowired
PermissionService permissionService;
/**
* 查询全部
* @return
*/
@RequestMapping("/findAll")
public ModelAndView findAll(){
//查询数据
List<Permission> permissionList = permissionService.findAll();
//创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("permissionList",permissionList);
//指定页面
modelAndView.setViewName("permission-list");
return modelAndView;
}
}
3、编写Service
接口
public interface PermissionService {
/**
* 查询全部
* @return
*/
List<Permission> findAll();
}
实现
@Service
public class PermissionServiceImpl implements PermissionService {
@Autowired
PermissionDao permissionDao;
@Override
public List<Permission> findAll() {
return permissionDao.findAll();
}
}
4、编写Dao
public interface PermissionDao {
/**
* 查询全部
* @return
*/
@Select("select * from sys_permission")
List<Permission> findAll();
}
第三节:添加权限功能-回显父菜单
1、页面入口
[外链图片转存失败(img-Cfh5h8Z9-1569005164731)(img\13.png)]
2、编写Controller
/**
* 添加数据回显
*
* 查询到所有的父权限返回
* @return
*/
@RequestMapping("/saveUI")
public ModelAndView saveUI(){
//查询数据
List<Permission> permissionList = permissionService.findAllParentPermission();
//创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("permissionList",permissionList);
//指定页面
modelAndView.setViewName("permission-add");
return modelAndView;
}
3、编写Service
接口
/**
* 查询所有的父权限
* @return
*/
List<Permission> findAllParentPermission();
实现
@Override
public List<Permission> findAllParentPermission() {
return permissionDao.findAllParentPermission();
}
4、编写Dao
/**
* 查询所有的父权限
* @return
*/
@Select("select * from sys_permission where pid = 0")
List<Permission> findAllParentPermission();
第四节:添加权限功能-保存到数据库
1、页面入口
[外链图片转存失败(img-82lSpMEV-1569005164732)(img\14.png)]
2、编写Controller
/**
* 保存权限
* @param permission
* @return
*/
@RequestMapping("/save")
public String save(Permission permission){
//保存操作
permissionService.save(permission);
//请求查询全部
return "redirect:/permission/findAll";
}
3、编写Service
接口
/**
* 保存权限
* @param permission
*/
void save(Permission permission);
实现
@Override
public void save(Permission permission) {
permissionDao.save(permission);
}
4、编写Dao
/**
* 保存权限
* @param permission
*/
@Insert("insert into sys_permission values(permission_seq.nextval,#{permissionName},#{url},#{pid})")
void save(Permission permission);