Spring Security教程(二):自定义数据库查询
Spring Security教程(二):自定义数据库查询
Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项目对用户信息和权限信息管理的要求。那么接下来就讲解如何自定义数据库实现对用户信息和权限信息的管理。
一、自定义表结构
这里还是用的mysql数据库,所以pom.xml文件都不用修改。这里只要新建三张表即可,user表、role表、user_role表。其中user用户表,role角色表为保存用户权限数据的主表,user_role为关联表。user用户表,role角色表之间为多对多关系,就是说一个用户可以有多个角色。ER图如下所示:
建表语句:
-- 角色
create table role(
id bigint,
name varchar(50),
descn varchar(200)
);
alter table role add constraint pk_role primary key(id);
alter table role alter column id bigint generated by default as identity(start with 1); -- 用户
create table user(
id bigint,
username varchar(50),
password varchar(50),
status integer,
descn varchar(200)
);
alter table user add constraint pk_user primary key(id);
alter table user alter column id bigint generated by default as identity(start with 1); -- 用户角色连接表
create table user_role(
user_id bigint,
role_id bigint
);
alter table user_role add constraint pk_user_role primary key(user_id, role_id);
alter table user_role add constraint fk_user_role_user foreign key(user_id) references user(id);
alter table user_role add constraint fk_user_role_role foreign key(role_id) references role(id);
插入数据:
insert into user(id,username,password,status,descn) values(1,'admin','admin',1,'管理员');
insert into user(id,username,password,status,descn) values(2,'user','user',1,'用户'); insert into role(id,name,descn) values(1,'ROLE_ADMIN','管理员角色');
insert into role(id,name,descn) values(2,'ROLE_USER','用户角色'); insert into user_role(user_id,role_id) values(1,1);
insert into user_role(user_id,role_id) values(1,2);
insert into user_role(user_id,role_id) values(2,2);
二、修改Spring Security的配置文件(applicationContext.xml)
现在我们要在这样的数据结构基础上使用Spring Security,Spring Security所需要的数据无非就是为了处理两种情况,一是判断登录用户是否合法,二是判断登陆的用户是否有权限访问受保护的系统资源。因此我们所要做的工作就是在现有数据结构的基础上,为Spring Security提供这两种数据。
在jdbc-user-service标签中有这样两个属性:
1. users-by-username-query为根据用户名查找用户,系统通过传入的用户名查询当前用户的登录名,密码和是否被禁用这一状态。
2.authorities-by-username-query为根据用户名查找权限,系统通过传入的用户名查询当前用户已被授予的所有权限。
同时通过代码提示能看到这两个属性的sql语句格式:
从图中可以看到第一个属性要的是通过username来查询用户名、密码和是否可用;第二个属性是通过username来查询用户权限,所以在我们自定义的表结构的基础上对sql语句进行修改,得到如下语句:
select username,password,status as enabled from user where username = ?
select user.username,role.name from user,role,user_role
where user.id=user_role.user_id and
user_role.role_id=role.id and user.username=?
这样最终得到的配置文件如下:
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <context:component-scan base-package="com.zy">
<!--application父容器步扫描controller注解,这样两个配置文件各自各负责各自的-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <!--数据库连接信息-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="" /> <!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" /> <!-- 配置初始化大小、最小、最大 -->
<property name="maxActive" value="" />
<property name="initialSize" value="" />
<property name="minIdle" value="" /> <!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="" /> <property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="" />
</bean> <!--配置mybatis-->
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlsessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定连接源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 指定映射文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 扫描指定包下的接口,自动生成代理类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zy.dao"/>
</bean> <!--<bean id="userService" class="com.zy.service.UserServiceImpl"></bean>-->
<!--security配置-->
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/admin.jsp" access="hasAnyRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/**" access="hasAnyAuthority('ROLE_ADMIN,ROLE_USER')"/>
<security:form-login authentication-success-forward-url="/index.jsp"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider> <!--使用自定义的表结构-->
<security:jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,status as enabled from user where username = ?"
authorities-by-username-query="select user.username,role.name from user,role,user_role where user.id=user_role.user_id and
user_role.role_id=role.id and user.username=?"/> <!--使用sucrity内置的内存数据库-->
<!--<security:user-service>-->
<!--<security:user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER"/>-->
<!--<security:user name="user" password="user" authorities="ROLE_USER"/>-->
<!--</security:user-service>-->
<security:password-encoder ref="noOpPasswordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
</beans>
三、自定义数据库查询的另外一种方式,实现UserDetailService,具体原理下一章会讲解
3.1自定义查询用户信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zy.dao.UserMapper">
<resultMap id="userInfo" type="com.zy.entity.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="password"/>
<collection property="roles" ofType="com.zy.entity.Role">
<id column="rid" property="id"/>
<result column="rname" property="name"/>
<result column="descn" property="descn"/>
</collection>
</resultMap>
<select id="findUserByName" parameterType="string" resultMap="userInfo">
select u.id id,u.username name,u.password password,r.id rid,r.name rname,r.descn descn from user u
LEFT JOIN user_role ur on u.id = ur.user_id
LEFT JOIN role r on ur.role_id = r.id
where username = #{name}
</select>
</mapper>
3.2写mapper接口
public interface UserMapper {
public User findUserByName(@Param("name") String name); }
自定义UserServiceImpl实现UserDetailService接口,并实现其方法
@Service("userService")
public class UserServiceImpl implements UserDetailsService{
@Autowired
private UserMapper userMapper; @Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User userByName = userMapper.findUserByName(s);
if (userByName==null){
return null;
}
UserDetails userDetails = new UserDetails() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<Role> roles = userByName.getRoles();
List<GrantedAuthority> list = new ArrayList<>();
if(!roles.isEmpty()){
for (Role role:roles){
list.add(new SimpleGrantedAuthority(role.getName()));
} } return list;
} @Override
public String getPassword() {
return userByName.getPassword();
} @Override
public String getUsername() {
return userByName.getName();
} @Override
public boolean isAccountNonExpired() {
return true;
} @Override
public boolean isAccountNonLocked() {
return true;
} @Override
public boolean isCredentialsNonExpired() {
return true;
} @Override
public boolean isEnabled() {
return true;
}
};
return userDetails;
}
}
3.4放入容器中后,再application.xml中配置相应的配置,这里就是自定义的查询权限。,这里的user-service-ref就是方才放入容器的UserServiceImpl
application.xnl的整体配置如下;
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <context:component-scan base-package="com.zy">
<!--application父容器步扫描controller注解,这样两个配置文件各自各负责各自的-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <!--数据库连接信息-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="" /> <!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" /> <!-- 配置初始化大小、最小、最大 -->
<property name="maxActive" value="" />
<property name="initialSize" value="" />
<property name="minIdle" value="" /> <!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="" /> <property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="" />
</bean> <!--配置mybatis-->
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlsessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定连接源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 指定映射文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 扫描指定包下的接口,自动生成代理类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zy.dao"/>
</bean> <!--<bean id="userService" class="com.zy.service.UserServiceImpl"></bean>-->
<!--security配置-->
<security:http auto-config="true">
<security:intercept-url pattern="/admin.jsp" access="hasAnyRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/login.html" access="permitAll()"/>
<security:intercept-url pattern="/**" access="hasAnyAuthority('ROLE_ADMIN,ROLE_USER')"/>
<security:form-login login-page="/login.html" username-parameter="username" password-parameter="password"
login-processing-url="/authentication/form" authentication-failure-url="/error.jsp"
authentication-success-forward-url="/index.jsp"/>
<security:logout logout-url="/logout_zy" invalidate-session="false" logout-success-url="/login.html"/>
<security:csrf disabled="true"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService"> <!--使用自定义的表结构-->
<!--<security:jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,status as enabled from user where username = ?"-->
<!--authorities-by-username-query="select user.username,role.name from user,role,user_role where user.id=user_role.user_id and -->
<!--user_role.role_id=role.id and user.username=?"/>-->
<!---->
<!--使用sucrity内置的内存数据库-->
<!--<security:user-service>-->
<!--<security:user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER"/>-->
<!--<security:user name="user" password="user" authorities="ROLE_USER"/>-->
<!--</security:user-service>-->
<security:password-encoder ref="bCryptPasswordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<!--<bean id="userServiceImpl" class="com.zy.service.UserServiceImpl">-->
<!--<security:intercept-methods>-->
<!--<security:protect method="" access=""/>-->
<!--</security:intercept-methods>-->
<!--</bean>-->
</beans>
三、结果
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.dao.UserMapper">
<resultMap id="userInfo" type="com.zy.entity.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="password"/>
<collection property="roles" ofType="com.zy.entity.Role">
<id column="rid" property="id"/>
<result column="rname" property="name"/>
<result column="descn" property="descn"/>
</collection>
</resultMap>
<select id="findUserByName" parameterType="string" resultMap="userInfo">
select u.id id,u.username name,u.password password,r.id rid,r.name rname,r.descn descn from user u
LEFT JOIN user_role ur on u.id = ur.user_id
LEFT JOIN role r on ur.role_id = r.id
where username = #{name}
</select>
</mapper>
Spring Security教程(二):自定义数据库查询的更多相关文章
- Spring Security教程(二):通过数据库获得用户权限信息
上一篇博客中,Spring Security教程(一):初识Spring Security,我把用户信息和权限信息放到了xml文件中,这是为了演示如何使用最小的配置就可以使用Spring Securi ...
- Spring Security教程(二)
上一篇博客中,Spring Security教程(一),我把用户信息和权限信息放到了xml文件中,这是为了演示如何使用最小的配置就可以使用Spring Security,而实际开发中,用户信息和权限信 ...
- spring security方法一 自定义数据库表结构
Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也无法满足所有企业内部对用户信息和权限信息管理的要求.基本上每个企业内部都有一套自己的用户信息管理结构,同时也 ...
- Spring Security教程(三):自定义表结构
在上一篇博客中讲解了用Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项 ...
- Spring Security教程(三)
在上一篇博客中讲解了用Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项 ...
- Spring Security教程(五):自定义过滤器从数据库从获取资源信息
在之前的几篇security教程中,资源和所对应的权限都是在xml中进行配置的,也就在http标签中配置intercept-url,试想要是配置的对象不多,那还好,但是平常实际开发中都往往是非常多的资 ...
- Spring Security教程系列(一)基础篇-1
第 1 章 一个简单的HelloWorld 第 1 章 一个简单的HelloWorld Spring Security中可以使用Acegi-1.x时代的普通配置方式,也可以使用从2.0时代才出现的命名 ...
- Spring Security教程(八):用户认证流程源码详解
本篇文章主要围绕下面几个问题来深入源码: 用户认证流程 认证结果如何在多个请求之间共享 获取认证用户信息 一.用户认证流程 上节中提到Spring Security核心就是一系列的过滤器链,当一个请求 ...
- Spring Security 解析(二) —— 认证过程
Spring Security 解析(二) -- 认证过程 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .S ...
随机推荐
- AE(ArcGIS Engine)的安装与配置(附加ArcGIS安装及所需安装包)
https://blog.csdn.net/qq_38281942/article/details/82534279
- C#常见几种集合比较
1. ArrayList 1.1 ArrayList是一个特殊数组,通过添加和删除元素就可以动态改变数组的长度. ArrayList集合相对于数组的优点:支持自动改变大小,可以灵活的插入元素,可以灵活 ...
- 解决for循环插入同一元素无法重复插入问题
var el = document.createElement("div"); el.appendChild(document.createTextNode("hello ...
- 图的最短路径算法Dijkstra算法模板
Dijkstra算法:伪代码 //G为图,一般设为全局变量,数组d[u]为原点到达个点的额最短路径, s为起点 Dijkstra(G, d[u], s){ 初始化: for (循环n次){ u = 是 ...
- Iris_xorm
xorm表基本操作及高级操作 表结构基本操作 对表结构的操作最常见的操作是查询和统计相关的方法,我们首先来看相关实现: 条件查询 Id值查询:参数接收主键字段的值.例如: var user User ...
- 爬虫(十一):selenium爬虫
1. selenium基础 selenium部分可以去看我写的selenium基础部分,由于链接太多了这里就不发出来了. 代理ip: 有时候频繁爬取一些网页.服务器发现你是爬虫后会封掉你的ip地址.这 ...
- 运行时错误'430': 类不支持自动化或不支持期望的接口。New ADODB.Connection问题
运行时错误'430': 类不支持自动化或不支持期望的接口. 问题: 经调试发现,是运行到下面语句时出错:Set MyConn = New ADODB.Connection 运行到这里是提示本错误 这 ...
- HPS—虚拟地址映射
HPS 如何对FPGA外设进行操作?hardware:在Qsys中将外设连接到AXI bridge上software:映射外设物理地址到到应用程序可以操作的虚拟地址,应用程序通过得到的虚拟地址入口控制 ...
- Codeforces Round #624 (Div. 3) C. Perform the Combo(前缀和)
You want to perform the combo on your opponent in one popular fighting game. The combo is the string ...
- vue项目用npm安装sass包遇到的问题及解决办法
IDEA启动vue程序,浏览器访问时出现如下情况的关于node-sass的错误: 错误1: Module build failed (from ./node_modules/sass-loader/d ...