CREATE TABLE users(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
email VARCHAR2(50) UNIQUE NOT NULL,
username VARCHAR2(50),
PASSWORD VARCHAR2(50),
phoneNum VARCHAR2(20),
STATUS INT
)
public class UserInfo {
private String id;
private String username;
private String email;
private String password;
private String phoneNum;
private int status;
private String statusStr;
private List roles;
}
CREATE TABLE role(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
roleName VARCHAR2(50) ,
roleDesc VARCHAR2(50)
)
public class Role {
private String id;
private String roleName;
private String roleDesc;
private List permissions;
private List users;
}
用户与角色之间是多对多关系,我们通过 user_role 表来描述其关联,在实体类中 User 中存在 List ,在 Role 中有 List.
而角色与权限之间也存在关系,我们会在后面介绍
CREATE TABLE users_role(
userId varchar2(32),
roleId varchar2(32),
PRIMARY KEY(userId,roleId),
FOREIGN KEY (userId) REFERENCES users(id),
FOREIGN KEY (roleId) REFERENCES role(id)
)
CREATE TABLE permission(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
permissionName VARCHAR2(50) ,
url VARCHAR2(50)
)
1.3.3 实体类
public class Permission {
private String id;
private String permissionName;
private String url;
private List roles;
}
权限资源与角色是多对多关系,我们使用 role_permission 表来描述。在实体类 Permission 中存在 List, 在 Role 类中有List
CREATE TABLE role_permission(
permissionId varchar2(32),
roleId varchar2(32),
PRIMARY KEY(permissionId,roleId),
FOREIGN KEY (permissionId) REFERENCES permission(id),
FOREIGN KEY (roleId) REFERENCES role(id)
)
Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。
( https://projects.spring.io/spring-security/ ) Spring Security 为基于 J2EE 企业应用软件提供了全面安全服务。特别是使用领先的J2EE 解决方案 -Spring 框架开发的企业软件项目。人们使用 Spring Security 有很多种原因,不过通常吸引他们的是在J2EE Servlet 规范或 EJB 规范中找不到典型企业应用场景的解决方案。 特别要指出的是他们不能再WAR 或 EAR 级别进行移植。这样,如果你更换服务器环境,就要,在新的目标环境进行大量的工作,对你的应用系统进行重新配 置安全。使用Spring Security 解决了这些问题,也为你提供很多有用的,完全可以指定的其他安全特性。 安全包括两个主要操作。
“ 认证 ” ,是为用户建立一个他所声明的主体。主题一般式指用户,设备或可以在你系 统中执行动作的其他系统。
“ 授权 ” 指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由 身份验证过程建立了。
这些概念是通用的,不是 Spring Security 特有的。在身份验证层面, Spring Security 广泛支持各种身份验证模式,这些验证模型绝大多数都由第三方提供,或则正在开发的有关标准机构提供的,例如 Internet Engineering Task Force.作为补充, Spring Security 也提供了自己的一套验证功能。
Spring Security 目前支持认证一体化如下认证技术: HTTP BASIC authentication headers ( 一个基于 IEFT RFC 的标准) HTTP Digest authentication headers ( 一个基于 IEFT RFC 的标准 ) HTTP X.509 client certifificate exchange(一个基于 IEFT RFC 的标准 ) LDAP ( 一个非常常见的跨平台认证需要做法,特别是在大环境 ) Form-based authentication (提供简单用户接口的需求 ) OpenID authentication Computer Associates Siteminder JA-SIG Central Authentication Service (CAS,这是一个流行的开源单点登录系统 ) Transparent authentication context propagation for Remote Method Invocation and HttpInvoker (一个 Spring 远程调用协议 )
Maven 依赖
org.springframework.security
spring-security-web
5.0.1.RELEASE
org.springframework.security
spring-security-config
5.0.1.RELEASE
4.0.0
spring_security_demo
SpringSecurity_quickStart
0.0.1-SNAPSHOT
war
5.0.2.RELEASE
5.0.1.RELEASE
org.springframework
spring-core
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework.security
spring-security-web
${spring.security.version}
org.springframework.security
spring-security-config
${spring.security.version}
javax.servlet
javax.servlet-api
3.1.0
provided
org.apache.maven.plugins
maven-compiler-plugin
3.2
1.8
UTF-8
org.apache.tomcat.maven
tomcat7-maven-plugin
8080
/
contextConfigLocation
classpath:spring-security.xml
org.springframework.web.context.ContextLoaderListener
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
我们在 webapp 下创建一个 index.html 页面,在页面中任意写些内容。
当我们访问 index.html 页面时发现会弹出登录窗口,可能你会奇怪,我们没有建立下面的登录页面,为什么 Spring Security会跳到上面的登录页面呢?这是我们设置 http 的 auto-confifig=”true” 时 Spring Security 自动为我们生成的。
2.2.5.1 spring-security.xml 配置
2.2.5.2 login.html
Insert title here
2.2.5.3 success.html
Insert title here
success html
退出
2.2.5.4 failer.html
Insert title here
登录失败
在 Spring Security 中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用 UserDetails 、 UserDetailsService来完成操作。
UserDetails
public interface UserDetails extends Serializable {
Collection extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
UserDetails 是一个接口,我们可以认为 UserDetails 作用是于封装当前进行认证的用户信息,但由于其是一个接口,所以我们可以对其进行实现,也可以使用Spring Security 提供的一个 UserDetails 的实现类 User 来完成操作
以下是 User 类的部分代码
public class User implements UserDetails, CredentialsContainer {
private String password;
private final String username;
private final Set authorities;
private final boolean accountNonExpired; //帐户是否过期
private final boolean accountNonLocked; //帐户是否锁定
private final boolean credentialsNonExpired; //认证是否过期
private final boolean enabled; //帐户是否可用
}
UserDetailsService
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
上面将 UserDetails 与 UserDetailsService 做了一个简单的介绍,那么我们具体如何完成 Spring Security 的数据库认证操作哪,我们通过用户管理中用户登录来完成Spring Security 的认证操作。
spring security 的配置
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
数据 - AdminLTE2定制版 | Log in
public interface IUserService extends UserDetailsService{
}
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = userDao.findByUsername(username);
List roles = userInfo.getRoles();
List authoritys = getAuthority(roles);
User user = new User(userInfo.getUsername(), "{noop}" + userInfo.getPassword(),
userInfo.getStatus() == 0 ? false : true, true, true, true, authoritys);
return user;
}
private List getAuthority(List roles) {
List authoritys = new ArrayList();
for (Role role : roles) {
authoritys.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return authoritys;
}
}
public interface IUserDao {
@Select("select * from user where id=#{id}")
public UserInfo findById(Long id) throws Exception;
@Select("select * from user where username=#{username}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(column = "username", property = "username"),
@Result(column = "email", property = "email"),
@Result(column = "password", property = "password"),
@Result(column = "phoneNum", property = "phoneNum"),
@Result(column = "status", property = "status"),
@Result(column = "id", property = "roles", javaType = List.class, many =
@Many(select = "com.itheima.ssm.dao.IRoleDao.findRoleByUserId")) })
public UserInfo findByUsername(String username);
}
使用 spring security 完成用户退出,非常简单
配置
页面中
注销
请在资料中查看具体代码
@Controller
@RequestMapping("/user")
public class UserControlller {
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
List users = userService.findAll();
ModelAndView mv = new ModelAndView();
mv.addObject("userlist", users);
mv.setViewName("user-list");
return mv;
}
}
@Select("select * from user")
public List findAll();
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
数据 - AdminLTE2定制版
用户管理 用户表单
@Controller
@RequestMapping("/user")
public class UserControlller {
@Autowired
private IUserService userService;
@RequestMapping("/save.do")
public String save(UserInfo user) throws Exception {
userService.save(user);
return "redirect:findAll.do";
}
}
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void save(UserInfo user) throws Exception {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userDao.save(user);
}
}
前期我们的用户密码没有加密,现在添加用户时,我们需要对用户密码进行加密
@Insert("insert into user(email,username,password,phoneNum,status) value(#{email},#
{username},#{password},#{phoneNum},#{status})")
public void save(UserInfo user) throws Exception;
请在资料中查看页面详细代码
注意:需要添加 js
$("#collapse-table").treetable({ expandable : true });
@Controller
@RequestMapping("/user")
public class UserControlller {
@Autowired
private IUserService userService;
@RequestMapping("/findById.do")
public ModelAndView findById(@RequestParam(name = "id", required = true) Long id) throws
Exception {
UserInfo user = userService.findById(id);
ModelAndView mv = new ModelAndView();
mv.addObject("user", user);
mv.setViewName("user-show");
return mv;
}
}
@Select("select * from user where id=#{id}")
@Results({ @Result(id = true, property = "id", column = "id"), @Result(column = "username",
property = "username"),
@Result(column = "email", property = "email"), @Result(column =
"password", property = "password"),
@Result(column = "phoneNum", property = "phoneNum"), @Result(column =
"status", property = "status"),
@Result(column = "id", property = "roles", javaType = List.class, many =
@Many(select = "com.itheima.ssm.dao.IRoleDao.findRoleByUserId")) })
public UserInfo findById(Long id) throws Exception;
@Select("select * from role where id in( select roleId from user_role where userId=#{userId})")
@Results(
{
@Result(id=true,column="id",property="id"),
@Result(column="roleName",property="roleName"),
@Result(column="roleDesc",property="roleDesc"),
@Result(column="id",property="permissions",javaType=List.class,many=@Many(select="com.itheima.ssm
.dao.IPermissionDao.findByRoleId"))
})
public List findRoleByUserId(Long userId);
我们需要将用户的所有角色及权限查询出来所以需要调用 IRoleDao 中的 fifindRoleByUserId, 而在 IRoleDao 中需要调用IPermissionDao 的 fifindByRoleId
@Select("select * from permission where id in (select permissionId from role_permission where
roleId=#{roleId})")
public List findByRoleId(Long roleId);
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
数据 - AdminLTE2定制版
@RequestMapping("/role")
@Controller
public class RoleController {
@Autowired
private IRoleService roleService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
List roleList = roleService.findAll();
ModelAndView mv = new ModelAndView();
mv.addObject("roleList", roleList);
mv.setViewName("role-list");
return mv;
}
@Select("select * from role")
public List findAll();
请在页面中查看详细代码
@RequestMapping("/role")
@Controller
public class RoleController {
@Autowired
private IRoleService roleService;
@RequestMapping("/save.do")
public String save(Role role) {
roleService.save(role);
return "redirect:findAll.do";
}
}
@Insert("insert into role(roleName,roleDesc) value(#{roleName},#{roleDesc})")
public void save(Role role);
请在资料中查看详细代码
@RequestMapping("/permission")
@Controller
public class PermissionController {
@Autowired
private IPermissionService permissionService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
List permissionList = permissionService.findAll();
ModelAndView mv = new ModelAndView();
mv.addObject("permissionList", permissionList);
mv.setViewName("permission-list");
return mv;
}
}
@Select("select * from permission")
public List findAll();
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
数据 - AdminLTE2定制版
资源权限管理 资源权限表单
@RequestMapping("/permission")
@Controller
public class PermissionController {
@Autowired
private IPermissionService permissionService;
@RequestMapping("/save.do")
public String save(Permission p) throws Exception {
permissionService.save(p);
return "redirect:findAll.do";
}
}
@Insert("insert into permission(permissionName,url) value(#{permissionName},#{url})")
public void save(Permission p);
用户与角色之间是多对多关系,我们要建立它们之间的关系,只需要在中间表 user_role 插入数据即可。
在 user-list.jsp 页面上添加链接
添加角色
展示可以添加角色的页面 user-role-add.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
数据 - AdminLTE2定制版
用户管理 添加角色表单
findUserByIdAndAllRole(Long id) 方法
此方法用于查找要操作的用户及可以添加的角色,参数是要操作的用户 id
@RequestMapping("/findUserByIdAndAllRole.do")
public ModelAndView findUserByIdAndAllRole(Long id) throws Exception {
UserInfo user = userService.findById(id);
List roleList = roleService.findOtherRole(id);
ModelAndView mv = new ModelAndView();
mv.addObject("user", user);
mv.addObject("roleList", roleList);
mv.setViewName("user-role-add");
return mv;
}
调用 IUserService 的 fifindById 方法获取要操作的 User
调用 IRoleService 的 fifindOtherRole 方法用于获取可以添加的角色信息
addRoleToUser(Long userId,Long[] ids) 方法些方法用于在用户与角色之间建立关系,参数userId 代表要操作的用户 id, 参数 ids 代表的是角色 id 数组
@RequestMapping("/addRoleToUser.do")
public String addRoleToUser(Long userId, Long[] ids) throws Exception {
userService.addRoleToUser(userId,ids);
return "redirect:findAll.do";
}
IRoleDao
@Select("select * from role where id not in( select roleId from user_role where userId=#
{id})")
public List findOtherRole(Long id);
用于查找可以添加的角色
IUserDao
@Insert("insert into user_role(userId,roleId) value(#{userId},#{roleId})")
public void addRoleToUser(@Param("userId") Long userId, @Param("roleId") Long roleId);
用于添加用户与角色关系
角色与权限之间是多对多关系,我们要建立它们之间的关系,只需要在中间表 role_permission 插入数据即可。
在 role-list.jsp 页面上添加链接
添加权限
展示可以添加权限的页面 role-permission-add.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
数据 - AdminLTE2定制版
角色管理 添加权限表单
findRoleByIdAndAllPermission(Long roleId) 方法
此方法用于查找要操作的角色及可以添加的权限,参数是要操作的角色 id
@RequestMapping("/findRoleByIdAndAllPermission.do")
public ModelAndView findRoleByIdAndAllPermission(@RequestParam(name = "id", required
= true) Long roleid)
throws Exception {
ModelAndView mv = new ModelAndView();
Role role = roleService.findById(roleid);
mv.addObject("role", role);
List permissionList =
permissionService.findOtherPermission(roleid);
mv.addObject("permissionList", permissionList);
mv.setViewName("role-permission-add");
return mv;
}
调用 IRoleService 的 fifindById 方法获取要操作的 Role
调用 IPermissionService 的 fifindOtherPermission 方法用于获取可以添加的权限信息
addPermissionToRole(Long roleId,Long[] ids) 方法
些方法用于在角色与权限之间建立关系,参数 roleId 代表要操作的角色 id, 参数 permissionIds 代表的是权限 id数组
@RequestMapping("/addPermissionToRole.do")
public String addPermissionToRole(@RequestParam(name = "roleId") Long roleId,
@RequestParam(name = "ids") Long[] permissionIds) throws Exception {
roleService.addPermissionToRole(roleId, permissionIds);
return "redirect:findAll.do";
}
IPermissionDao
@Select("select * from permission where id not in (select permissionId from role_permission
where roleId=#{roleId})")
public List findOtherPermission(Long roleid);
用于查找可以添加的权限
IRoleDao
@Insert("insert into role_permission (roleId,permissionId) value (#{roleId},#
{permissionId})")
public void addPermissionToRole(@Param("roleId") Long roleId, @Param("permissionId") Long
permissionId);
用于绑定角色与权限的关系
在服务器端我们可以通过 Spring security 提供的注解对方法来进行权限控制。 Spring Security 在方法的权限控制上支持三种类型的注解,JSR-250 注解、 @Secured 注解和支持表达式的注解,这三种注解默认都是没有启用的,需要单独通过global-method-security 元素的对应属性进行启用
配置文件
注解开启
@EnableGlobalMethodSecurity : Spring Security 默认是禁用注解的,要想开启注解,需要在继承
WebSecurityConfifigurerAdapter 的类上加 @EnableGlobalMethodSecurity 注解,并在该类中将
AuthenticationManager 定义为 Bean 。
@RolesAllowed 表示访问对应方法时所应该具有的角色
示例:
@RolesAllowed({"USER", "ADMIN"}) 该方法只要具有 "USER", "ADMIN" 任意一种权限就可以访问。这里可以省略前缀ROLE_ ,实际的权限可能是 ROLE_ADMIN
@PermitAll 表示允许所有的角色进行访问,也就是说不进行权限控制
@DenyAll 是和 PermitAll 相反的,表示无论什么角色都不能访问
@PreAuthorize 在方法调用之前 , 基于表达式的计算结果来限制对方法的访问
示例:
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(@P("userId") long userId ){ }
这里表示在 changePassword 方法执行之前,判断方法参数 userId 的值是否等于 principal 中保存的当前用户的
userId ,或者当前用户是否具有 ROLE_ADMIN 权限,两种符合其一,就可以访问该方法。
@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
示例:
@PostAuthorize
User getUser("returnObject.userId == authentication.principal.userId or
hasPermission(returnObject, 'ADMIN')");
@PostFilter 允许方法调用 , 但必须按照表达式来过滤方法的结果
@PreFilter 允许方法调用 , 但必须在进入方法之前过滤输入值
@Secured 注解标注的方法进行权限控制的支持,其值默认为 disabled
示例:
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("ROLE_TELLER")
在 jsp 页面中我们可以使用 spring security 提供的权限标签来进行权限控制
maven 导入
org.springframework.security
spring-security-taglibs
version
页面导入
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
在 jsp 中我们可以使用以下三种标签,其中 authentication 代表的是当前认证对象,可以获取当前认证对象信息,例如用户名。其它两个标签我们可以用于权限控制
6.4.2.1 authentication
hasPermission : hasPermission 属性用于指定以逗号分隔的权限列表
domainObject : domainObject 用于指定对应的域对象
var : var 则是用以将鉴定的结果以指定的属性名存入 pageContext 中,以供同一页面的其它地方使用