SpringSecurity菜鸟教程
一:简单配置权限管理
SecurityConfg的配置
package com.example.demo11.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.Objects;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return Objects.equals(charSequence.toString(), s);
}
};
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("用户").password("123").roles("vip1")
.and()
.withUser("管理员").password("123").roles("vip2")
.and()
.withUser("超级管理员").password("123").roles("vip1", "vip2");
}
//配置忽略掉的 URL 地址,一般用于js,css,图片等静态资源
@Override
public void configure(WebSecurity web) throws Exception {
//web.ignoring() 用来配置忽略掉的 URL 地址,一般用于静态文件
web.ignoring().antMatchers("/js/**", "/css/**", "/fonts/**", "/images/**", "/lib/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/js/**", "/images/**").permitAll();
//开启运行iframe嵌套页面
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers("/level1/vip1").hasRole("vip1")
.antMatchers("/level2/vip2").hasRole("vip2");
//没有权限会到默认的登录页面
http.formLogin();
}
}
IndexController的代码
package com.example.demo11.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("/index")
public String index(){
return "index";
}
@GetMapping("/level1/vip1")
public String level1Vip1(){
return "level1/vip1";
}
@GetMapping("/level2/vip2")
public String level2Vip1(){
return "level2/vip2";
}
}
由于没有设置springsecurity全部拦截,主页可以允许所有人访问
二:自定义登录页面,记住密码
1自定义登陆页面
改变SecurityConfig中的配置
这个需要自己写一个登录的接口
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/js/**", "/images/**").permitAll();
//开启运行iframe嵌套页面
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers("/level1/vip1").hasRole("vip1")
.antMatchers("/level2/vip2").hasRole("vip2");
//任何请求都必须经过身份认证
http.authorizeRequests().anyRequest().authenticated();
//没有权限会到默认的登录页面
http.formLogin()
//登录的页面
.loginPage("/login")
.usernameParameter("username")//自定义表单的用户名的name,默认为username
.passwordParameter("password")//自定义表单的密码的name,默认为password
.loginProcessingUrl("/dologin")//表单请求的地址,一般与form的action属性一致,注意:不用自己写doLogin接口,只要与form的action属性一致即可
.successForwardUrl("/index")//登录成功后跳转的页面(重定向)
.failureForwardUrl("/login")//登录失败后跳转的页面(重定向)
.and()
.logout() //开启注销功能
.logoutSuccessUrl("/login") //注销后跳转到哪一个页面
.clearAuthentication(true)// 配置注销登录请求URL为"/logout"(默认也就是 /logout)
.clearAuthentication(true) // 清除身份认证信息
.invalidateHttpSession(true) //使Http会话无效
.permitAll()
.and().csrf().disable();
}
login.html文件
2.记住密码和注销功能
//开启记住我功能,cookie接收,默认保存两周,自定义接收其前端
http.rememberMe().rememberMeParameter("remember");
注销功能:
三:基于数据库自定义的表单验证
1.数据库表
这里的登录认证只涉及到三张表:用户表(user)、角色表(role)、用户角色中间表(user_role)。
/*
Navicat Premium Data Transfer
Source Server : test3
Source Server Type : MySQL
Source Server Version : 80015
Source Host : localhost:3306
Source Schema : test2
Target Server Type : MySQL
Target Server Version : 80015
File Encoding : 65001
Date: 31/05/2020 22:01:56
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci 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 role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_vip0');
INSERT INTO `role` VALUES (2, 'ROLE_vip1');
INSERT INTO `role` VALUES (3, 'ROLE_vip2');
INSERT INTO `role` VALUES (4, 'ROLE_vip3');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci 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 user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq');
INSERT INTO `user` VALUES (3, '灰太狼', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq');
INSERT INTO `user` VALUES (4, '喜羊羊', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq');
INSERT INTO `user` VALUES (5, '懒羊羊', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq');
INSERT INTO `user` VALUES (6, '小灰灰', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 1, 3);
INSERT INTO `user_role` VALUES (4, 1, 4);
INSERT INTO `user_role` VALUES (5, 3, 2);
INSERT INTO `user_role` VALUES (6, 4, 3);
INSERT INTO `user_role` VALUES (7, 6, 4);
INSERT INTO `user_role` VALUES (8, 5, 1);
SET FOREIGN_KEY_CHECKS = 1;
注意:这里的role跟上面的例子相比多加了ROLE_前缀。这是因为之前的role都是通过springsecurity的api赋值过去的,他会自行帮我们加上这个前缀。但是现在我们使用的是自己的数据库里面读取出来的权限,然后封装到自己的实体类中。所以这时候需要我们自己手动添加这个ROLE_前缀。经过测试如果不加ROLE_前缀的话,可以做数据库的认证,但无法做授权
2.建实体类User,注意User需要实现UserDetails接口,并且实现该接口下的7个接口
package com.example.demo11.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {
private Integer id;
private String userName;
private String passWord;
private List<Role> roles;//该用户对应的角色
/**
* 返回用户的权限集合。
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles){
authorities.add(new SimpleGrantedAuthority(role.getName()));
System.out.println(authorities);
}
return authorities;
}
/**
* 返回账号的密码
* @return
*/
@Override
public String getPassword() {
return passWord;
}
/**
* 返回账号的用户名
* @return
*/
@Override
public String getUsername() {
return userName;
}
/**
* 账号是否失效,true:账号有效,false账号失效。
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账号是否被锁,true:账号没被锁,可用;false:账号被锁,不可用
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 账号认证是否过期,true:没过期,可用;false:过期,不可用
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 账号是否可用,true:可用,false:不可用
* @return
*/
@Override
public boolean isEnabled() {
return true;
}
}
角色表实体类Role,这个类不用实现上述接口
package com.zsc.po;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
private Integer id;
private String name;//角色的名字
}
接下来做数据库的查询,创建持久层接口(UserMapper和RoleMapper)
package com.example.demo.mapper;
import com.example.demo.pojo.Role;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface RoleMapper {
/**
* 通过用户id获取用户角色集合
*
* @param userId 用户id
* @return List<Role> 角色集合
*/
List<Role> getRolesByUserId(Integer userId);
}
package com.example.demo.mapper;
import com.example.demo.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface UserMapper {
/**
* 通过用户名获取用户信息
*
* @param username 用户名
* @return User 用户信息
*/
List<User> getUserByUsername(String username);
}
持久层接口对应配置文件(UserMapper.xml和RoleMapper.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.example.demo.mapper.RoleMapper">
<resultMap id="roleMap" type="com.example.demo.pojo.Role">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
</resultMap>
<select id="getRolesByUserId" resultMap="roleMap">
select * from role r,user_role ur where r.id = ur.rid and ur.uid = #{userId}
</select>
</mapper>
<?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.example.demo.mapper.UserMapper">
<resultMap id="userMap" type="com.example.demo.pojo.User">
<id column="id" property="id"></id>
<result column="username" property="userName"></result>
<result column="password" property="passWord"></result>
<collection property="roles" ofType="com.example.demo.pojo.Role">
<id property="id" column="rid"></id>
<result column="rname" property="name"></result>
</collection>
</resultMap>
<select id="getUserByUsername" resultMap="userMap">
select * from user where username = #{username}
</select>
</mapper>