上一篇对SpringSecurity做了基本介绍,以及做了一个基于内存用户认证的小栗子,在企业开发中,都会连接数据库来做认证,那怎么来基于数据库做认证呢,今天我们就动手练习下,我们使用的环境呢,springboot+springsecurity+mybaits+mysql来搭建。
没有mysql数据库环境的小伙伴,可以看下我之前的文章,来安装mysql
docker安装mysql
安装完数据库后,创建一个名为SpringSecurity的schema数据库,导入两个表,一个是认证用的表,一个就是授权用的表
create table sys_user (
uid int(11) not null
,username varchar(12) not null
,password varchar(120) not null
,status int(1)
,rid int(11) not null
);
create table sys_role (
rid int(11) not null
,role_name varchar(30)
,role_desc varchar(60)
);
接下来,还是创建一个javaweb的项目,上一篇也有提到,登录spring官网的创建项目页面https://start.spring.io/,添加依赖时,我们选择Lombok,Spring Web,Thymeleaf,Spring Security,MyBatis Framework,MySQL Driver等,如果还有自己常用的依赖的话,添加进去,完成后使用idea或者eclipse导入环境,确认pom文件,下边的依赖被加上。
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.2
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.security
spring-security-test
test
我们使用mybatis来操作数据库,我们来编写mapper文件,这里我们就用注解的方式,来写sql文。
package com.example.demo.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;import com.example.demo.domain.SysUser;
public interface UserMapper {
@Select("select * from sys_user where username = #{username}")
@Results({
@Result(id = true, property = "uid", column = "uid"),
@Result(property = "roles", column = "uid", javaType = List.class, many = @Many(select = "com.example.demo.mapper.RoleMapper.findByUid"))
})
public SysUser findByName(String username);
}
package com.example.demo.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import com.example.demo.domain.SysRole;
public interface RoleMapper {
@Select("select r.rid rid, r.role_name roleName, r.role_desc roleDesc from sys_role r, sys_user u where u.uid = #{uid} and r.rid = u.rid")
public ListfindByUid(Integer uid);
}
mapper文件就是做两个事情,一个是验证用户名和密码,在一个就是,校验成功后,取得该用户权限,下边我们编写service,由于SpringSecurity为我们提供了用户验证的UserDetailsService接口,我们就来创建自己的service来继承并实现它,添加我们自己的业务处理。
package com.example.demo.service;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface UserService extends UserDetailsService {
}
package com.example.demo.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;@Service
public class UserServiceImpl implements UserService {@Autowired
private UserMapper userMapper;@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userMapper.findByName(username);
}}
这里代码不是很复杂,就是调用下我们已经写好的mapper文件,传入用户名,实现数据库验证,我们继续往下说,来介绍下SpringSecurity的配置类。
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import com.example.demo.service.UserService;
@Configurable
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}@Autowired
private UserService userDetailsService;@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication().withUser("user")
// .password("{noop}123") // {noop}
// .roles("USER");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login.html", "/css/**", "/js/**").permitAll().antMatchers("/**")
.hasAnyRole("USER", "ADMIN", "PRODUCT").anyRequest().authenticated()
.and().formLogin().loginPage("/login.html")
.loginProcessingUrl("/login").successForwardUrl("/index")
.failureForwardUrl("/failer.html").permitAll().and().logout().logoutUrl("/logout")
.logoutSuccessUrl("/login.html").invalidateHttpSession(true).permitAll().and().csrf().disable();
}}
代码中有注释的部分,就是我们上篇文章提到的,将用户名和密码保存到内存中的认证方式,实现数据库验证呢,就是将我们自己写好的service赋值给SpringSecurity,设定service的同时,由于密码保存的是密文,也要设定一种加密的方式,在内部做校验,SpringSecurity框架做的很强大,帮我们做了很多事情。
再说说授权的事情,SpringSecurity为我们提供了@EnableGlobalMethodSecurity(securedEnabled = true)注解,这个注解类中有三种认证方式,项目中只能开启一种,开启两种的话,配置权限只会一个管用
- prePostEnabled
可以写SpringEL表达式的权限管理方式,例如:@PreAuthorize("hasAnyRole('user')")
- securedEnabled
SpringSecurity默认的权限管理方式,例如:@Secured({ "ROLE_user", "ROLE_admin" })
- jsr250Enabled
支持jdk层面的注解
这里我开启了SpringSecurity默认的权限管理注解,它是怎么使用的呢,我们就创建一个Controller类,用户只有ROLE_PRODUCT的权限时才能访问。
package com.example.demo.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class ProductController {@Secured("ROLE_PRODUCT")
@RequestMapping("/products")
public String products() {
return "products";
}}
这里还有一点需要注意的一点是,数据库中保存的密码都是密文,由于是测试程序,我们需要自己手动写一个程序,来将明文密码转换为密文密码,保存到数据库中。
package com.example.demo.config;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class Test {
public static void main(String[] args) {
System.err.println(new BCryptPasswordEncoder().encode("123"));
}
}
输出的结果
$2a$10$vNZ2pvTCM84Ajx8WgIh8B.oYpaRgqGrST.UCHS3jxNwcB/MT7N6Ui
到这里,SpringSecurity的数据库认证以及授权的方法就介绍完了,在每一个项目中,都会涉及到认证和授权,并且也不单单只是本文介绍的内容,小伙伴们多查资料,了解了SpringSecurity会省去我们很多自己写的代码。