Spring Security基于Mysql身份验证(自定义认证,基于角色),由于业务的复杂性我们不可能使用spring官网的example来做认证,本次主要是重写security的认证方式,阅读源码即可实现。
下一篇为Spring Security自定义授权:
技术栈:
springboot+springsecurity+mybatis
所用最新框架spring5,2019年11月17日 14:51:16
项目基于上一篇文章springsecurity内存认证授权https://www.jianshu.com/p/d71c7fa9b534
一、首先创建项目springsecurity-mysql
image.pngimage.png
页面基于上篇文章:https://www.jianshu.com/p/d71c7fa9b534
maven如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lingkang</groupId>
<artifactId>springsecurity-mysql</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springsecurity-mysql</name>
<description>2019年11月17日 15:32:40</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--html页面使用sec标签,属于spring5 security-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建数据库名称为security和两张表:用户表(se_user)、角色表(se_role):
DROP TABLE IF EXISTS `se_role`;
CREATE TABLE `se_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NULL DEFAULT NULL,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of se_role
-- ----------------------------
INSERT INTO `se_role` VALUES (1, 1, 'vip1', '2019-11-17 15:27:04', NULL);
INSERT INTO `se_role` VALUES (3, 2, 'vip2', '2019-11-17 15:27:26', NULL);
INSERT INTO `se_role` VALUES (4, 2, 'vip3', '2019-11-17 15:27:35', NULL);
INSERT INTO `se_role` VALUES (5, 3, 'vip1', '2019-11-17 15:28:00', NULL);
INSERT INTO `se_role` VALUES (6, 3, 'vip2', '2019-11-17 15:28:08', NULL);
INSERT INTO `se_role` VALUES (7, 3, 'vip3', '2019-11-17 15:28:16', NULL);
-- ----------------------------
-- Table structure for se_user
-- ----------------------------
DROP TABLE IF EXISTS `se_user`;
CREATE TABLE `se_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`status` int(1) NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`update_titme` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of se_user
-- ----------------------------
INSERT INTO `se_user` VALUES (1, '张三', '123', '123', 1, '2019-11-17 15:26:10', NULL);
INSERT INTO `se_user` VALUES (2, '李四', 'user', '123', 1, '2019-11-17 15:26:28', NULL);
INSERT INTO `se_user` VALUES (3, '管理员', 'admin', 'admin', 1, '2019-11-17 15:26:46', NULL);
application.properties配置如下:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/security?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
#配置xml扫描
mybatis.mapper-locations=classpath:mapper/*.xml
搭建项目如下
image.png
二、修改代码
创建自定义CustomUserDetailsServiceImpl :
@Component //交给spring托管,应为我们使用@Autowired注入了spring的接口
public class CustomUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//查询用户
SeUser user = userService.getUserInfo(s);
if (user == null) {
throw new UsernameNotFoundException("帐号:" + s + " 不存在!");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
//查询拥有的角色
List<SeRole> roles = userService.getUserRoleByUserId(user.getId());
roles.forEach(r -> {
//security的验证规则需要ROLE_,由于我数据库是vip1、vip2、vip3这种值,需要加上ROLE_
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + r.getName()));
});
//spring5+要求密码加密,我这里传明文密码来加密
String password = new BCryptPasswordEncoder().encode(user.getPassword());
return new User(user.getUsername(), password, grantedAuthorities);
}
}
修改SecurityConfig:
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//注入我们加密的方式,spring5+以上必须要加密密码,这是官网推荐的加密方式,也可选择MD5
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//由于我们自定义了UserDetailsService,我们需要新注入这个bean
@Bean
@Override //重写
public UserDetailsService userDetailsServiceBean() {
//使用我们自定义的 UserDetailsService
return new CustomUserDetailsServiceImpl();
}
//认证----自定义认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//自定义认证
auth.userDetailsService(userDetailsServiceBean());
}
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//授权规则,除了要授权的,其他所有人能访问
http.authorizeRequests()
.antMatchers("/vip1/**").hasAnyRole("vip1")
.antMatchers("/vip2/**").hasAnyRole("vip2")
.antMatchers("/vip3/**").hasAnyRole("vip3")
.anyRequest().permitAll(); //其他页面所有人能访问
//启动登陆页面
//定制登陆页面,表单提交的路径loginProcessingUrl
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
//注销功能 ,跳回首页
//关闭跨域认证请求,否则你需要post来注销
http.logout().logoutSuccessUrl("/")
.and().csrf().disable();
//开启记住我功能,表单提交remember的参数
http.rememberMe().rememberMeParameter("remember");
}
}
项目总体如下:
image.png
三、效果如下:
登录123时:
image.png
登录user时:
image.png
登录admin时:
image.png
想要从session中获取用户信息可以:
SecurityContextImpl securityContextImpl = (SecurityContextImpl) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
一般spring security在认证后,security会把一个SecurityContextImpl对象存储到session中,此对象中有当前用户的各种资料。
项目源码:https://github.com/xcocean/springsecurity-mysql
网友评论