Springboot詳解整合SpringSecurity實(shí)現(xiàn)全過程
使用Basic認(rèn)證模式
1、maven依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<!-- SpringBoot整合Web組件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- springboot整合freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-->spring-boot 整合security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>2、SecurityConfig 配置類
@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 添加授權(quán)賬戶
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 設(shè)置用戶賬號(hào)信息和權(quán)限
auth.inMemoryAuthentication().withUser("kaico_admin").password("kaico")
.authorities("/");
// 如果kaico_admin賬戶權(quán)限的情況 所有的接口都可以訪問,如果kaico_add 只能訪問addMember
auth.inMemoryAuthentication().withUser("kaico_add").password("kaico")
.authorities("/");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置httpBasic Http協(xié)議認(rèn)證
http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic();
}
/**
* There is no PasswordEncoder mapped for the id "null"
* 原因:升級(jí)為Security5.0以上密碼支持多中加密方式,恢復(fù)以前模式
*
* @return
*/
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}3、測(cè)試controller接口
@Controller
public class IndexController {
/**
* 跳轉(zhuǎn)到首頁
*
* @return
*/
@RequestMapping("/")
public String index() {
return "index";
}
@ResponseBody
@RequestMapping("/addMember")
public String addMember() {
return "新增用戶";
}
@ResponseBody
@RequestMapping("/delMember")
public String delMember() {
return "刪除用戶";
}
@ResponseBody
@RequestMapping("/updateMember")
public String updateMember() {
return "修改用戶";
}
@ResponseBody
@RequestMapping("/showMember")
public String showMember() {
return "查詢用戶";
}
}使用form表形式登錄
在上面Basic認(rèn)證模式的基礎(chǔ)上修改SecurityConfig配置類,修改下面的方法
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置httpBasic Http協(xié)議認(rèn)證
http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin();
}springSecurity默認(rèn)提供登錄頁面,如下圖所示:

注意:使用表單登錄之后,登錄成功之后默認(rèn)跳轉(zhuǎn)首頁,也就是請(qǐng)求路徑/,沒有該請(qǐng)求路徑的話會(huì)報(bào)錯(cuò)。
實(shí)現(xiàn)權(quán)限控制
上面的案例只是實(shí)現(xiàn)了登錄認(rèn)證,但是還沒有實(shí)現(xiàn)權(quán)限控制。
在上面的基礎(chǔ)上修改
1、修改SecurityConfig配置類
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 設(shè)置用戶賬號(hào)信息和權(quán)限
auth.inMemoryAuthentication().withUser("kaico_admin").password("kaico")
.authorities("addMember", "delMember", "updateMember", "showMember"
);
// 如果kaico_admin賬戶權(quán)限的情況 所有的接口都可以訪問,如果kaico_add 只能訪問addMember
auth.inMemoryAuthentication().withUser("kaico_add").password("kaico")
.authorities("addMember");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置權(quán)限
http.authorizeRequests().antMatchers("/addMember").hasAnyAuthority("addMember")
.antMatchers("/addMember").hasAnyAuthority("addMember")
.antMatchers("/delMember").hasAnyAuthority("delMember")
.antMatchers("/updateMember").hasAnyAuthority("updateMember")
.antMatchers("/showMember").hasAnyAuthority("showMember")
.antMatchers("/**").fullyAuthenticated().and().formLogin();
}沒有權(quán)限報(bào)403錯(cuò)誤

修改403權(quán)限不足頁面
1、增加配置類
@Configuration
public class WebServerAutoConfiguration {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400");
ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401");
ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403");
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415");
ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
return factory;
}
}2、增加對(duì)應(yīng)的錯(cuò)誤請(qǐng)求路徑
@RestController
public class ErrorController {
@RequestMapping("/error/403")
public String error403(){
return "您當(dāng)前訪問的接口權(quán)限不足";
}
}
自定義登錄頁面
1、登錄頁面準(zhǔn)備
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h1>權(quán)限控制登陸系統(tǒng)</h1>
<form action="/login" method="post">
<span>用戶名稱</span><input type="text" name="username"/> <br>
<span>用戶密碼</span><input type="password" name="password"/> <br>
<input type="submit" value="登陸">
</form>
<#if RequestParameters['error']??>
用戶名稱或者密碼錯(cuò)誤
</#if>
</body>
</html>2、修改SecurityConfig配置類
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置權(quán)限
http.authorizeRequests().antMatchers("/addMember").hasAnyAuthority("addMember")
.antMatchers("/addMember").hasAnyAuthority("addMember")
.antMatchers("/delMember").hasAnyAuthority("delMember")
.antMatchers("/updateMember").hasAnyAuthority("updateMember")
.antMatchers("/showMember").hasAnyAuthority("showMember")
.antMatchers("/login").permitAll() //放行登錄請(qǐng)求頁面
.antMatchers("/**").fullyAuthenticated()
.and().formLogin().loginPage("/login").and().csrf().disable();
}3、增加登錄頁面請(qǐng)求controller
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(){
return "login";
}
}
結(jié)合數(shù)據(jù)庫實(shí)現(xiàn)RBAC權(quán)限模型權(quán)限控制
環(huán)境準(zhǔn)備
數(shù)據(jù)庫
RBAC模型相關(guān)數(shù)據(jù)庫表關(guān)系圖

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(10) NOT NULL,
`permName` varchar(50) DEFAULT NULL,
`permTag` varchar(50) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL COMMENT '請(qǐng)求url',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1', '查詢用戶', 'showMember', '/showMember');
INSERT INTO `sys_permission` VALUES ('2', '添加用戶', 'addMember', '/addMember');
INSERT INTO `sys_permission` VALUES ('3', '修改用戶', 'updateMember', '/updateMember');
INSERT INTO `sys_permission` VALUES ('4', '刪除用戶', 'delMember', '/delMember');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(10) NOT NULL,
`roleName` varchar(50) DEFAULT NULL,
`roleDesc` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'admin', '管理員');
INSERT INTO `sys_role` VALUES ('2', 'add_user', '添加管理員');
-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
`role_id` int(10) DEFAULT NULL,
`perm_id` int(10) DEFAULT NULL,
KEY `FK_Reference_3` (`role_id`),
KEY `FK_Reference_4` (`perm_id`),
CONSTRAINT `FK_Reference_4` FOREIGN KEY (`perm_id`) REFERENCES `sys_permission` (`id`),
CONSTRAINT `FK_Reference_3` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` VALUES ('1', '2');
INSERT INTO `sys_role_permission` VALUES ('1', '3');
INSERT INTO `sys_role_permission` VALUES ('1', '4');
INSERT INTO `sys_role_permission` VALUES ('2', '2');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(10) NOT NULL,
`username` varchar(50) DEFAULT NULL,
`realname` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`createDate` date DEFAULT NULL,
`lastLoginTime` date DEFAULT NULL,
`enabled` int(5) DEFAULT NULL,
`accountNonExpired` int(5) DEFAULT NULL,
`accountNonLocked` int(5) DEFAULT NULL,
`credentialsNonExpired` int(5) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'kaico_admin', '張三', 'c2c92733a75333a4bac673632ff7519a', '2018-11-13', '2018-11-13', '1', '1', '1', '1');
INSERT INTO `sys_user` VALUES ('2', 'kaico_add', '小余', 'c2c92733a75333a4bac673632ff7519a', '2018-11-13', '2018-11-13', '1', '1', '1', '1');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` int(10) DEFAULT NULL,
`role_id` int(10) DEFAULT NULL,
KEY `FK_Reference_1` (`user_id`),
KEY `FK_Reference_2` (`role_id`),
CONSTRAINT `FK_Reference_2` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`),
CONSTRAINT `FK_Reference_1` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '2');maven依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<!-- SpringBoot整合Web組件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- springboot整合freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-->spring-boot 整合security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- springboot 整合mybatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- alibaba的druid數(shù)據(jù)庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>yml配置
# 配置freemarker
spring:
freemarker:
# 設(shè)置模板后綴名
suffix: .ftl
# 設(shè)置文檔類型
content-type: text/html
# 設(shè)置頁面編碼格式
charset: UTF-8
# 設(shè)置頁面緩存
cache: false
# 設(shè)置ftl文件路徑
template-loader-path:
- classpath:/templates
# 設(shè)置靜態(tài)文件路徑,js,css等
mvc:
static-path-pattern: /static/**
datasource:
name: test
url: jdbc:mysql://www.kaicostudy.com:3306/study_springSecurity
username: root
password: 123456
# druid 連接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
java代碼
實(shí)體類
// 用戶信息表
@Data
public class UserEntity implements UserDetails {
private Integer id;
private String username;
private String realname;
private String password;
private Date createDate;
private Date lastLoginTime;
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
// 用戶所有權(quán)限
private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
}// 角色信息表
@Data
public class RoleEntity {
private Integer id;
private String roleName;
private String roleDesc;
}@Data
public class PermissionEntity {
private Integer id;
// 權(quán)限名稱
private String permName;
// 權(quán)限標(biāo)識(shí)
private String permTag;
// 請(qǐng)求url
private String url;
}mapper類
@Mapper
public interface PermissionMapper {
@Select(" select * from sys_permission ")
List<PermissionEntity> findAllPermission();
}
@Mapper
public interface UserMapper {
/**
* 根據(jù)用戶名稱查詢
*
* @param userName
* @return
*/
@Select(" select * from sys_user where username = #{userName}")
UserEntity findByUsername(@Param("userName") String userName);
/**
* 查詢用戶的權(quán)限根據(jù)用戶查詢權(quán)限
*
* @param userName
* @return
*/
@Select(" select permission.* from sys_user user" + " inner join sys_user_role user_role"
+ " on user.id = user_role.user_id inner join "
+ "sys_role_permission role_permission on user_role.role_id = role_permission.role_id "
+ " inner join sys_permission permission on role_permission.perm_id = permission.id where user.username = #{userName};")
List<PermissionEntity> findPermissionByUsername(@Param("userName") String userName);
}實(shí)現(xiàn)springSecurity的接口UserDetailsService
@Component
@Slf4j
public class MemberUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
/**
* loadUserByUserName
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1.根據(jù)該用戶名稱查詢?cè)跀?shù)據(jù)庫中是否存在
UserEntity userEntity = userMapper.findByUsername(username);
if (userEntity == null) {
return null;
}
// 2.查詢對(duì)應(yīng)的用戶權(quán)限
List<PermissionEntity> listPermission = userMapper.findPermissionByUsername(username);
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
listPermission.forEach(user -> {
authorities.add(new SimpleGrantedAuthority(user.getPermTag()));
});
log.info(">>>authorities:{}<<<", authorities);
// 3.將該權(quán)限添加到security
userEntity.setAuthorities(authorities);
return userEntity;
}
}密碼加密工具類
public class MD5Util {
private static final String SALT = "kaico";
public static String encode(String password) {
password = password + SALT;
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
throw new RuntimeException(e);
}
char[] charArray = password.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
}SecurityConfig配置類
@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MemberUserDetailsService memberUserDetailsService;
/**
* 添加授權(quán)賬戶
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 設(shè)置用戶賬號(hào)信息和權(quán)限
auth.userDetailsService(memberUserDetailsService).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
//對(duì)密碼做加密
return MD5Util.encode((String) rawPassword);
}
/**
*
* @param charSequence 用戶輸入的密碼
* @param s 數(shù)據(jù)庫字段中加密好的密碼
* @return
*/
@Override
public boolean matches(CharSequence charSequence, String s) {
String rawPass = MD5Util.encode((String) charSequence);
boolean result = rawPass.equals(s);
return result;
}
});
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置權(quán)限
http.authorizeRequests().antMatchers("/addMember").hasAnyAuthority("addMember")
.antMatchers("/addMember").hasAnyAuthority("addMember")
.antMatchers("/delMember").hasAnyAuthority("delMember")
.antMatchers("/updateMember").hasAnyAuthority("updateMember")
.antMatchers("/showMember").hasAnyAuthority("showMember")
.antMatchers("/login").permitAll() //放行登錄請(qǐng)求頁面
.antMatchers("/**").fullyAuthenticated()
.and().formLogin().loginPage("/login").and().csrf().disable();
}
}整合完成,開始測(cè)試。
動(dòng)態(tài)綁定數(shù)據(jù)庫所有權(quán)限
在上面代碼的基礎(chǔ)上實(shí)現(xiàn),上面是在代碼中配置請(qǐng)求路徑和權(quán)限標(biāo)識(shí)的綁定,現(xiàn)在改成在數(shù)據(jù)庫中動(dòng)態(tài)配置。
SecurityConfig配置類
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置權(quán)限
List<PermissionEntity> allPermission = permissionMapper.findAllPermission();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
expressionInterceptUrlRegistry = http.authorizeRequests();
allPermission.forEach((permission) -> {
expressionInterceptUrlRegistry.antMatchers(permission.getUrl()).
hasAnyAuthority(permission.getPermTag());
});
expressionInterceptUrlRegistry.antMatchers("/login").permitAll()
.antMatchers("/**").fullyAuthenticated()
.and().formLogin().loginPage("/login").and().csrf().disable();
}到此這篇關(guān)于Springboot詳解整合SpringSecurity實(shí)現(xiàn)全過程的文章就介紹到這了,更多相關(guān)Springboot SpringSecurity內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot安全框架整合SpringSecurity實(shí)現(xiàn)方式
- springboot整合security和vue的實(shí)踐
- springboot整合springsecurity與mybatis-plus的簡(jiǎn)單實(shí)現(xiàn)
- SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制
- SpringBoot整合SpringSecurityOauth2實(shí)現(xiàn)鑒權(quán)動(dòng)態(tài)權(quán)限問題
- SpringBoot整合SpringSecurity實(shí)現(xiàn)JWT認(rèn)證的項(xiàng)目實(shí)踐
- SpringBoot整合Security權(quán)限控制登錄首頁
- SpringBoot整合Spring?Security過濾器鏈加載執(zhí)行流程源碼分析(最新推薦)
- SpringBoot整合SpringSecurity和JWT和Redis實(shí)現(xiàn)統(tǒng)一鑒權(quán)認(rèn)證
- SpringBoot整合SpringSecurity認(rèn)證與授權(quán)
- SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用
相關(guān)文章
Java后臺(tái)Controller實(shí)現(xiàn)文件下載操作
這篇文章主要介紹了Java后臺(tái)Controller實(shí)現(xiàn)文件下載操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
uploadify java實(shí)現(xiàn)多文件上傳和預(yù)覽
這篇文章主要為大家詳細(xì)介紹了java結(jié)合uploadify實(shí)現(xiàn)多文件上傳和預(yù)覽的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
基于swagger參數(shù)與實(shí)體中參數(shù)不一致的原因分析
這篇文章主要介紹了基于swagger參數(shù)與實(shí)體中參數(shù)不一致的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11

