我们在企业级开发中, 用户只有在登录了账号才可以对表单中的数据进行相关的操作, 且不同身份角色的用户登录后对数据执行的权限也应该有所不同. 并且这些用户登录的账号不可以自己注册, 需要有权限的管理人员添加用户以及给该用户授予所具备的相关的权限. 下面我们先来实现用户登录后才可以操作数据的功能.
我们还是基于上一篇文章进行编写:https://blog.csdn.net/weixin_42629433/article/details/83342148
一. 在数据库中创建最高权限管理员
-- 用户表
CREATE TABLE users(
id INT(20) NOT NULL AUTO_INCREMENT,
username VARCHAR(50),
PASSWORD VARCHAR(50),
phoneNum VARCHAR(20),
email VARCHAR(50),
STATUS INT,
PRIMARY KEY (id)
)
INSERT INTO users VALUES (NULL, 'Zenith', '123456', '153********', '8123*****@qq.com',1);
-- 角色表
CREATE TABLE role(
id INT(20) NOT NULL AUTO_INCREMENT,
roleName VARCHAR(50) ,
roleDesc VARCHAR(50),
PRIMARY KEY (id)
)
INSERT INTO role VALUES (NULL, 'Manager', '最高权限管理员');
-- 用户角色关联表
CREATE TABLE users_role(
userId VARCHAR(32),
roleId VARCHAR(32)
)
INSERT INTO users_role VALUES ('1', '1');
二. 相关文件配置
1. 导入 SpringSecurity 框架实现功能的相关依赖 :
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
2. 在 web.xml 中引入 SpringSecurity 框架的配置 :
<!--Spring Security-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3. 在 resources 下创建 spring-security.xml 文件 :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 配置不拦截的静态资源 -->
<security:http pattern="/login.jsp" security="none"/>
<security:http pattern="/failure.jsp" security="none"/>
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<security:http pattern="/plugins/**" security="none"/>
<!--配置具体的规则:
auto-config="true" 使用框架提供默认登录
use-expressions="false" 是否使用SPEL表达式-->
<security:http auto-config="true" use-expressions="false">
<!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人所具有的角色" -->
<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN,ROLE_MANAGER"/>
<!-- 定义跳转的具体的页面 -->
<security:form-login
login-page="/login.jsp"
login-processing-url="/login.do"
default-target-url="/index.jsp"
authentication-failure-url="/failure.jsp"
authentication-success-forward-url="/pages/main.jsp"
/>
<!-- 关闭跨域请求 -->
<security:csrf disabled="true"/>
<!-- 退出页面 -->
<security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />
</security:http>
<!-- 切换成数据库中的用户名和密码 -->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<!-- 配置加密的方式-->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
4. 在 web.xml 中声明引入 spring-security.xml 文件至核心配置文件
<!--指定相关配置文件的路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml, classpath*:spring-security.xml</param-value>
</context-param>
三. 后台代码编写
1. 登录密码加密的工具类 :
package com.cast.domain;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Scanner;
/**
* 给密码加密的工具类
*/
public class BCryptPasswordEncoderUtils {
private static BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
public static String encodePassword(String password){
return bCryptPasswordEncoder.encode(password);
}
public static void main(String[] args) {
System.out.println("请输入要加密的密码: ");
Scanner input = new Scanner(System.in);
String password = input.nextLine();
String pwd = encodePassword(password);
System.out.println("加密后的密码为: ");
System.out.print(pwd);
}
}
2. 在 service 层创建一个 IUserService 接口来继承 UserDetailsService
package com.cast.service;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface IUserService extends UserDetailsService {
}
3. 在 service 层创建一个 UserServiceImpl 类实现 IUserService 接口
package com.cast.service.impl;
import com.cast.dao.IUserDao;
import com.cast.domain.Role;
import com.cast.domain.UserInfo;
import com.cast.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = null;
try {
userInfo = userDao.findByUsername(username);
} catch (Exception e) {
e.printStackTrace();
}
//将自己从数据库中查询到的Users对象封装到UserDetails中
User user = new User(userInfo.getUsername(), userInfo.getPassword(), userInfo.getStatus() == 0 ? false : true, true, true, true, getAuthority(userInfo.getRoles()));
return user;
}
//定义一个方法 将从数据库中获取到的所有角色装入集合
public List<SimpleGrantedAuthority> getAuthority(List<Role> roles) {
List<SimpleGrantedAuthority> list = new ArrayList<SimpleGrantedAuthority>();
for (Role role : roles) {
list.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
}
return list;
}
}
4. 在 domain 子工程中声明 users role 的实体封装类 :
package com.cast.domain;
import java.util.List;
/**
* 与数据库中 users 表对应
*/
public class UserInfo {
private String id;
private String username;
private String password;
private String phoneNum;
private String email;
private int status;
private String statusStr;
private List<Role> roles;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getStatusStr() {
return statusStr;
}
public void setStatusStr(String statusStr) {
this.statusStr = statusStr;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
package com.cast.domain;
import java.util.List;
/**
* 与数据库中 role 表对应
*/
public class Role {
private String id;
private String roleName;
private String roleDesc;
private List<Permission> permissions;
private List<UserInfo> users;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
public List<Permission> getPermissions() {
return permissions;
}
public void setPermissions(List<Permission> permissions) {
this.permissions = permissions;
}
public List<UserInfo> getUsers() {
return users;
}
public void setUsers(List<UserInfo> users) {
this.users = users;
}
}
5. 在 dao 层创建一个 IUserDao 接口
package com.cast.dao;
import com.cast.domain.UserInfo;
import org.apache.ibatis.annotations.*;
/**
* 登录用户的 dao 层接口
*/
public interface IUserDao {
//根据用户名查询用户
@Select("select * from users where username = #{username}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "password", column = "password"),
@Result(property = "phoneNum", column = "phoneNum"),
@Result(property = "email", column = "email"),
@Result(property = "status", column = "status"),
@Result(property = "roles", column = "id", javaType = java.util.List.class,
many = @Many(select = "com.cast.dao.IRoleDao.findByUserId"))
})
public UserInfo findByUsername(String username) throws Exception;
}
6. 在 dao 层创建 IRoleDao
package com.cast.dao;
import com.cast.domain.Role;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 用户的角色的 dao 层接口
*/
public interface IRoleDao {
//根据用户id查询所有对应角色
@Select("select * from role where id in (select roleId from users_role where userId = #{userId})")
public List<Role> findByUserId(String userId) throws Exception;
}
三. 前台页面核心代码
1. login.jsp 登录界面的 form 表单 :
<form action="${pageContext.request.contextPath}/login.do" method="post">
<div class="form-group has-feedback">
<input type="text" name="username" class="form-control" placeholder="用户名">
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="密码">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label><input type="checkbox"> 记住 下次自动登录</label>
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">登录</button>
</div>
<!-- /.col -->
</div>
</form>
2. 登录失败后的 failure.jsp 页面提示返回到登录页面的超链接 :
<a href="${pageContext.request.contextPath}/login.jsp">返回到登录页面</a>
代码下载: https://pan.baidu.com/s/17zb834Qy1P5T3BiV7ivRig
***此代码需在 jdk1.8 下运行使用***