SpringSecurity学习之基于数据库的用户认证

时间:2023-03-09 19:28:17
SpringSecurity学习之基于数据库的用户认证

  SpringSecurity给我们提供了一套最基本的认证方式,可是这种方式远远不能满足大多数系统的需求。不过好在SpringSecurity给我们预留了许多可扩展的接口给我们,我们可以基于这些接口实现自己的认证方式。

一、前期准备工作

1.1、创建示例数据库

Student表:

create table student
(
id int auto_increment
primary key,
stuName varchar(8) null,
password varchar(16) null,
joinTime datetime null,
clz_id int null
)
;

Classes(班级)表:

create table classes
(
id int auto_increment
primary key,
clz_name varchar(16) not null
)
;

1.2、添加相关依赖

compile group: 'mysql', name: 'mysql-connector-java'
compile group: 'org.springframework.security', name: 'spring-security-taglibs'
compile('org.springframework.boot:spring-boot-starter-jdbc')

二、实现步骤

2.1  定义Student类

package com.bdqn.lyrk.security.study.app.pojo;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User; import java.sql.Timestamp;
import java.util.Collection; public class Student extends User { private Timestamp joinTime; public Timestamp getJoinTime() {
return joinTime;
} public void setJoinTime(Timestamp joinTime) {
this.joinTime = joinTime;
} public Student(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
} }

  在这里定义的类继承User,User是SpringSecurity里的一个类,用以描述一个用户和其最基本的属性,当然我们要扩展它的用户我们也可以实现UserDetails接口

2.2 实现UserDetailsService

package com.bdqn.lyrk.security.study.app.service;

import com.bdqn.lyrk.security.study.app.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service; import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; @Service
public class UserService implements UserDetailsService { @Autowired
private JdbcTemplate jdbcTemplate; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User.UserBuilder users = User.withDefaultPasswordEncoder();
Map<String, Object> map = jdbcTemplate.queryForMap("select t.clz_name,t1.stuName,t1.password,t1.joinTime from student t1 inner join classes t on t.id = t1.clz_id where stuName = ?", username);
Timestamp joinTime = null;
if (map != null && map.size() > 0) {
String stuName = (String) map.get("stuName");
String password = (String) map.get("password");
joinTime = (Timestamp) map.get("joinTime");
String clzName = (String) map.get("clz_name");
users.password(password);
users.username(stuName);
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(clzName);
List<GrantedAuthority> list = new ArrayList<>();
list.add(authority);
users.authorities(list);
}
UserDetails userDetails = users.build();
Student student = new Student(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
// UserDetails userDetails = User.withDefaultPasswordEncoder().
student.setJoinTime(joinTime);
return student;
}
}

  在这个接口里我们要实现根据用户名查找用户的方法,那么一般情况下我们都会根据自己系统的用户表来获取用户信息,这里面注意几个方面:

  1)需要设置PasswordEncoder

  2) 需要设置其角色信息,那么在这里我用班级来表示用户的角色

  3)用户的三个重要属性就是 用户名,密码与权限

  4) 这里的返回值(UserDetails)不能返回null,如果根据用户名找不到对应的用户可以抛出UsernameNotFoundException异常

2.3 改造WebSecurityConfig

package com.bdqn.lyrk.security.study.app.config;

import com.bdqn.lyrk.security.study.app.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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; /**
* spring-security的相关配置
*
* @author chen.nie
* @date 2018/6/7
**/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private UserService userService; @Override
protected void configure(HttpSecurity http) throws Exception {
/*
1.配置静态资源不进行授权验证
2.登录地址及跳转过后的成功页不需要验证
3.其余均进行授权验证
*/
http.
authorizeRequests().antMatchers("/static/**").permitAll().
and().authorizeRequests().antMatchers("/user/**").hasRole("7022").
and().authorizeRequests().anyRequest().authenticated().
and().formLogin().loginPage("/login").successForwardUrl("/toIndex").permitAll()
.and().logout().logoutUrl("/logout").invalidateHttpSession(true).deleteCookies().permitAll()
;
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//设置自定义userService
auth.userDetailsService(userService);
}
}

在这里主要做以下处理:

 1)针对于/user/**路径的请求需要设置对应的权限

 2) 做用户注销的处理,用户注销时需要销毁session与cookie

 3)配置自定义UserDetailService

2.4、改造index.jsp

<%--
Created by IntelliJ IDEA.
User: chen.nie
Date: 2018/6/8
Time: 上午9:56
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
欢迎:${user.username}
<sec:authorize access="hasRole('7022')">
加入时间:${user.joinTime}
</sec:authorize>
<form action="/logout" method="post">
<input type="submit" value="退出" />
<sec:csrfInput/>
</form> </body>
</html>

  在这里面我们使用spring对security标签的支持判断当前用户是否有对应的角色,另外我们在处理登出操作时必须为post提交且有对应的token防止csrf