Spring Security连接数据库
我们先随便创建一个数据库
导入我们security的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--引入mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--mqsql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--引入jdbc、-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--引入模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
我创建的是一个web项目,我还用了thymeleaf模板引擎,用于后的界面的跳转,因为thymeleaf它是idea推荐的,idea默认比较点多,但是真的写项目的时候,我不推荐thymeleaf,它很坑,反正我被坑过,写一些简单的跳转还可以,因为thymeleaf它的界面访问直接就给我们配置了一个默认路径classpath:/templates 我们只需要把我们创建的页面放进templates就好
然后我们创建一个实体类
package com.kuang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName User
* @Description TODO
* @Author ht
* @Date 2020/8/20 10:23
* @Version 1.0
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
private String limit;
}
Security它不用写那个service层的接口,我们在添加进入我们自己的项目的时候我们可以单独创建一个Service的实现类用于Security,因为Security的实现类他是直接继承了UserDetailsService的接口,我们是直接重写它的loadUserByUsername方法就好
这就是我的文件的结构路径,根本没有service接口
接下来我们写service的实现类serviceimpl
@Service
//重写UserDetailsService的loadUserByUsername方法
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserMapper mapper;
@Autowired
PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User user =mapper.selectlimit(name);
if (user==null){
return null;
}else {
//创建一个权限的集合
Collection<GrantedAuthority> authorities = new ArrayList<>();
//添加获取权限
authorities.add(new SimpleGrantedAuthority(user.getLimit()));
//把对象信息(用户名,密码,权限)存入对象,返回该对象,controller层直接调用
org.springframework.security.core.userdetails.User user2 =new org.springframework.security.core.userdetails.User(user.getUsername(), passwordEncoder.encode(user.getPassword()), authorities);
System.out.println("管理员信息:"+user.getUsername()+" "+passwordEncoder.encode(user.getPassword())+" "+user2.getAuthorities());
return user2;
}
}
}
因为我是直接连接的数据库,所有这边我讲解一下,先把Mapper层写下来
@Mapper
@Repository
public interface UserMapper {
public User selectlimit(String username);
}//
还有我们与Mapper层相映射的mapper.xml文件
<?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.kuang.mapper.UserMapper">
<resultMap id="user" type="com.kuang.pojo.User">
<result property="id" column="id"/>
<result property="username" column="name"/>
<result property="password" column="password"/>
<result property="limit" column="limit"/>
</resultMap>
<!-- 获取权限-->
<select id="selectlimit" resultMap="user">
select * from user where name=#{username};
</select>
</mapper>
以及我们的Controller层
package com.kuang.controller;
import com.kuang.serviceimpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName HelloWorld
* @Description TODO
* @Author ht
* @Date 2020/8/12 16:45
* @Version 1.0
**/
@Controller
public class HelloController{
@Autowired
UserDetailsServiceImpl userDetailsService;
@GetMapping("/user")
public String success(String name){
// userDetailsService.loadUserByUsername(name);
return "success";
}
@GetMapping("/root")
public String vip(String name){
// userDetailsService.loadUserByUsername(name);
return "vip";
}
@GetMapping("/not")
public String not(String name) {
userDetailsService.loadUserByUsername(name);
return "not";
}
}
Controller层就是一些界面的跳转,不用在意这些
还有就是我们的首页,
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
================================
<hr>
<a th:href="@{/user}" th:text="user"></a>
<a th:href="@{/root}" th:text="vip"></a>
</body>
</html>
我们要让有user权限的进入user界面,有root权限的进入vip界面,注意啦这个只是一个路径,不是权限,权限我们的在config配置类中配置
Security的配置类是特别特别重要的东西,它里面写了认证以及授权
package com.kuang.config;
import com.kuang.serviceimpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @ClassName SecurityConfig
* @Description TODO
* @Author ht
* @Date 2020/8/12 16:57
* @Version 1.0
**/
@EnableWebSecurity
//WebSecurityConfigurerAdapter自定义的security策略
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//链式编程
@Override
//授权
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
//请求授权规则
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/root").hasRole("ROOT");
//没有权限自动跳转到登陆页(自带登录页)
//默认是username,password
//usernameParameter("user").passwordParameter("pwd").loginPage("/login");设置字段名以及跳转的接口
http.formLogin();
}
/*
认证规则
*/
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//在这里完成获得数据库中的用户信息
//密码一定要加密
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
这里面我做了很多的注释,大家应该看得懂,但是我还是讲一下,虽然我也是初学者,增加记忆吧
现在我们需要的东西都写完了。。。。额还有一个yaml配置文件,
spring:
datasource:
username: 数据库用户名
password: 数据库密码
url: jdbc:mysql:"数据库的url:数据库"?serverTimezone=UTC&characterEncoding=UTF-8&userUnicode=true&autoReconnect=true
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
#扫描配置mapper.xml的配置文件
mapper-locations: classpath:mybatis/*.xml
#设置别名,在mapper.xml中使用
type-aliases-package: com.kuang.pojo
下面我们来好好理一理
我们访问我们的接口,它没有权限那么它就会直接访问了security自带的登陆界面,需要我们登陆用户验证
//没有权限自动跳转到登陆页(自带登录页)
//默认是username,password
//usernameParameter("user").passwordParameter("pwd").loginPage("/login");设置字段名以及跳转的接口
http.formLogin();
我们只需要开启它,不过好像security好像会自带开启,可以试试
大家是不是很纳闷,我们访问页面的时候我们并没有访问什么方法啊,我们是直接返回的一个界面呀,应该就不管security的事情啊,但是我们的给Securitry配置了confg纳闷它就会启动,具体的大家可以去看源码,它就会生效,而他生效,大家可以看到
/*
认证规则
*/
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//在这里完成获得数据库中的用户信息
//密码一定要加密
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
在这堆代码里我们是不是可以看到一个很熟悉的东西,那就是我们的重写的UserDetailsServic接口的实现类,我们将其注入了这个类中,使用它,然后回到我们的UserDetailsServiceImpl实现类又注入了UserMapper,UserMapper又映射了UserMapper.xml文件,这样是不是就把我们的数据库连上了,但是注意了,密码必须加密,加密过后,ok就差不多了
我们继续说逻辑
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User user =mapper.selectlimit(name);
if (user==null){
return null;
}else {
//创建一个权限的集合
Collection<GrantedAuthority> authorities = new ArrayList<>();
//添加获取权限
authorities.add(new SimpleGrantedAuthority(user.getLimit()));
//把对象信息(用户名,密码,权限)存入对象,返回该对象,controller层直接调用
org.springframework.security.core.userdetails.User user2 =new org.springframework.security.core.userdetails.User(user.getUsername(), passwordEncoder.encode(user.getPassword()), authorities);
System.out.println("管理员信息:"+user.getUsername()+" "+passwordEncoder.encode(user.getPassword())+" "+user2.getAuthorities());
return user2;
}
我们通过传入的name查询到我们的对象user它实体类User定义的,这里有一个很严肃的问题,就是loadUserByUsername这个方法,它只能接收一个参数,而且它里面自带了一个实体类User,我就着了道,但是我没改,我想给大家提个醒所有我用的这样的
org.springframework.security.core.userdetails.User user2 =new org.springframework.security.core.userdetails.User(user.getUsername(), passwordEncoder.encode(user.getPassword()), authorities);
loadUserByUsername的这个User类,有三个参数,username,password,以及authorities(权限),我们这边都是通过我们自己通过查询得到的对象user来传入数据
我们将数据传入进去,然后那边调用验证就好
有一个十分十分值得注意的地方超级坑,就是它的这个权限,你必须在你的权限前面加一个ROLE_就是这样
然后你在Config里面进行认证的时候它才会人,不然就是403,我试过很多次,在网上也看了很多东西,都没搞出个所以然,好啦,大部门就都讲完了
可以大家一看很多觉得比较乱,但是自己看了我觉得还是很不错的
写了博客,有全面仔细了解了一遍,自己感觉收货超大;