Spring Security教程(二):自定义数据库查询

Spring Security教程(二):自定义数据库查询

 

Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项目对用户信息和权限信息管理的要求。那么接下来就讲解如何自定义数据库实现对用户信息和权限信息的管理。

一、自定义表结构

这里还是用的mysql数据库,所以pom.xml文件都不用修改。这里只要新建三张表即可,user表、role表、user_role表。其中user用户表,role角色表为保存用户权限数据的主表,user_role为关联表。user用户表,role角色表之间为多对多关系,就是说一个用户可以有多个角色。ER图如下所示:

Spring Security教程(二):自定义数据库查询_第1张图片

 

建表语句:

复制代码
-- 角色
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语句格式:
Spring Security教程(二):自定义数据库查询_第2张图片

从图中可以看到第一个属性要的是通过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=?

这样最终得到的配置文件如下:

"1.0" encoding="UTF-8"?>
"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">

    base-package="com.zy">
        
        "annotation" expression="org.springframework.stereotype.Controller"/>
    

    
    "dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        
        "driverClassName" value="com.mysql.jdbc.Driver" />
        "url" value="jdbc:mysql://localhost:3306/test" />
        "username" value="root" />
        "password" value="123456" />

        
        "filters" value="stat" />

        
        "maxActive" value="20" />
        "initialSize" value="1" />
        "minIdle" value="1" />

        
        "maxWait" value="60000" />

        
        "timeBetweenEvictionRunsMillis" value="60000" />

        
        "minEvictableIdleTimeMillis" value="300000" />

        "testWhileIdle" value="true" />
        "testOnBorrow" value="false" />
        "testOnReturn" value="false" />

        
        "poolPreparedStatements" value="true" />
        "maxOpenPreparedStatements" value="20" />
    
    
    
    
    "sqlsessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        
        "dataSource" ref="dataSource"/>
        
        "mapperLocations" value="classpath:mapper/*.xml"/>
    
    
    class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    "basePackage" value="com.zy.dao"/>
    
    
    
    
    "true" use-expressions="true">
        "/admin.jsp" access="hasAnyRole('ROLE_ADMIN')"/>
        "/**" access="hasAnyAuthority('ROLE_ADMIN,ROLE_USER')"/>
        "/index.jsp"/>
    
    
        
            
            
            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=?"/>
            
            
            
                
                
            
            ref="noOpPasswordEncoder"/>
        
    
    "noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>

 

三、自定义数据库查询的另外一种方式,实现UserDetailService,具体原理下一章会讲解

3.1自定义查询用户信息

"1.0" encoding="UTF-8" ?>
"-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
namespace="com.zy.dao.UserMapper">
    "userInfo" type="com.zy.entity.User">
        "id" property="id"/>
        "name" property="name"/>
        "password" property="password"/>
        "roles" ofType="com.zy.entity.Role">
            "rid" property="id"/>
            "rname" property="name"/>
            "descn" property="descn"/>
        
    
<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>

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 getAuthorities() {
                List roles = userByName.getRoles();
                List 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的整体配置如下;

"1.0" encoding="UTF-8"?>
"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">

    base-package="com.zy">
        
        "annotation" expression="org.springframework.stereotype.Controller"/>
    

    
    "dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        
        "driverClassName" value="com.mysql.jdbc.Driver" />
        "url" value="jdbc:mysql://localhost:3306/test" />
        "username" value="root" />
        "password" value="123456" />

        
        "filters" value="stat" />

        
        "maxActive" value="20" />
        "initialSize" value="1" />
        "minIdle" value="1" />

        
        "maxWait" value="60000" />

        
        "timeBetweenEvictionRunsMillis" value="60000" />

        
        "minEvictableIdleTimeMillis" value="300000" />

        "testWhileIdle" value="true" />
        "testOnBorrow" value="false" />
        "testOnReturn" value="false" />

        
        "poolPreparedStatements" value="true" />
        "maxOpenPreparedStatements" value="20" />
    
    
    
    
    "sqlsessionfactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        
        "dataSource" ref="dataSource"/>
        
        "mapperLocations" value="classpath:mapper/*.xml"/>
    
    
    class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    "basePackage" value="com.zy.dao"/>
    
    
    
    
    "true">
        "/admin.jsp"  access="hasAnyRole('ROLE_ADMIN')"/>
        "/login.html" access="permitAll()"/>
        "/**" access="hasAnyAuthority('ROLE_ADMIN,ROLE_USER')"/>
        "/login.html" username-parameter="username" password-parameter="password" 
                             login-processing-url="/authentication/form" authentication-failure-url="/error.jsp" 
                             authentication-success-forward-url="/index.jsp"/>
        "/logout_zy" invalidate-session="false"  logout-success-url="/login.html"/>
        "true"/>
    
    
        ref="userService">
            
            
            
                                        
    
            
            
            
                
                
            
            ref="bCryptPasswordEncoder"/>
        
    
    "bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
    
        
            
        
    

 

三、结果

由于只是换了用户信息和权限信息保存的方式,效果和之前教程的效果是一样的。
xml version="1.0" encoding="UTF-8" ?>
mapperPUBLIC"-//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教程(二):自定义数据库查询)