SpringSecurity简单教程(源码开源免费提供)
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;@Configurationpublic 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;@Controllerpublic 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@NoArgsConstructorpublic 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@AllArgsConstructorpublic 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@Repositorypublic interface RoleMapper { /** * 通过用户id获取用户角色集合 * * @param userId 用户id * @return List 角色集合 */ 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@Repositorypublic 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>
源码地址:SpringSecurity