官方介绍:Spring Security是一个功能强大且可高度自定义的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。 Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正强大之处在于它可以轻松扩展以满足自定义要求。
Spring Security是一个为基于Spring的企业应用系统提供声明式的安全访问控制解决方式的安全框架(简单说是对访问权限进行控制),应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。Spring Security的主要核心功能为 认证和授权,所有的架构也是基于这两个核心功能去实现的。
用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
特征:
想要对对Web资源进行保护,最好的办法莫过于Filter,要想对方法调用进行保护,最好的办法莫过于AOP。所以Spring Security在我们进行用户认证以及授予权限的时候,通过各种各样的拦截器来控制权限的访问,从而实现安全。
Spring Security 框架的主要过滤器(Filter) :
想要对对Web资源进行保护,最好的办法莫过于Filter,要想对方法调用进行保护,最好的办法莫过于AOP。所以Spring Security在我们进行用户认证以及授予权限的时候,通过各种各样的拦截器来控制权限的访问,从而实现安全。
Spring Security 框架的主要过滤器(Filter) :
Spring Security框架的核心组件:
(1)JDK版本:1.8
(2)Spring Boot:2.1.2.RELEASE
(3)Spring Security 5.1.3
(4)IntelliJ IDEA 2016.3.4
建表语句如下,用户对应的密码已经使用md5加密。
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(255) DEFAULT NULL COMMENT '姓名',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`roles` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户表';
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'ouyang', 'bbd126f4856c57f68d4e30264da6a4e6', 'ROLE_ADMIN,ROLE_USER');
INSERT INTO `user` VALUES ('2', 'admin', 'bbd126f4856c57f68d4e30264da6a4e6', 'ROLE_ADMIN');
INSERT INTO `user` VALUES ('3', 'user', 'bbd126f4856c57f68d4e30264da6a4e6', 'ROLE_USER');
依赖pom.xml:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.2.RELEASE
com.oycbest
springsecuritytest
0.0.1-SNAPSHOT
springsecuritytest
springsecurity 安全认证框架实战项目
1.8
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.apache.commons
commons-lang3
3.8.1
org.springframework.boot
spring-boot-devtools
true
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
mysql
mysql-connector-java
5.1.29
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
org.springframework.boot
spring-boot-maven-plugin
application.yml配置:
server:
port: 8082
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/springsecurity?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=utf8
username: ouyangcheng
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
initialSize: 1
minIdle: 1
maxActive: 50
maxWait: 6000
timeBetweenEvictionRunsMillis: 6000
minEvictableIdleTimeMillis: 30000
testWhileIdle: true
testOnBorrow: true
testOnReturn: true
validationQuery: SELECT 1 from dual
connectionProperties: config.decrypt=false
package com.oycbest.config;
import com.oycbest.domain.User;
import com.oycbest.service.PasswordEncoder;
import com.oycbest.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.annotation.Resource;
/**
* @Author: oyc
* @Date: 2019/1/29 13:45
* @Description:
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserService userService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//允许基于HttpServletRequest使用限制访问
http.authorizeRequests()
//不需要身份认证
.antMatchers("/", "/home","/toLogin","/**/customer/**").permitAll()
.antMatchers("/js/**", "/css/**", "/images/**", "/fronts/**", "/doc/**", "/toLogin").permitAll()
.antMatchers("/user/**").hasAnyRole("USER")
//.hasIpAddress()//读取配置权限配置
.antMatchers("/**").access("hasRole('ADMIN')")
.anyRequest().authenticated()
//自定义登录界面
.and().formLogin().loginPage("/toLogin").loginProcessingUrl("/login").failureUrl("/toLogin?error").permitAll()
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.and().exceptionHandling().accessDeniedPage("/toLogin?deny")
.and().httpBasic()
.and().sessionManagement().invalidSessionUrl("/toLogin")
.and().csrf().disable();
}
}
这个类主要是实现了loadUserByname方法, 然后我们可以在这个类中注入我们的service、repository或者mapper接口, 然后方法内部根据username获得该用户, 然后再获取这个用户的权限。通过用户名到数据库进行查询,如果没有查到这个用户,则登录失败留在原网页,如果查出这个用户,则把用户的账号密码和查出的用户拥有的所有权限,封装到security自己的User类中返回(得到的形式是这样的org.springframework.security.core.userdetails.User@c41325a7: Username: ouyang; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN,ROLE_USER),接下来security会把前台发过来的密码和查到的密码进行比较,看是否登录成功,失败停留在原网页,这一步框架自己解决,不需要我们来判断,只需要返回User即可。
我这里直接注入UserRepository,使用UserRepository直接获取用户的信息和权限,对UserRepository有疑问可参考上节Spring Boot入门系列八(SpringBoot 整合SpringData JPA操作Mysql数据库): https://blog.csdn.net/u014553029/article/details/86662518
package com.oycbest.service;
import com.oycbest.domain.User;
import com.oycbest.repository.UserRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: oyc
* @Date: 2019/1/29 14:19
* @Description: 用户服务类
*/
@Service
public class UserService implements UserDetailsService {
@Resource
private UserRepository repository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
User user = repository.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
//用户权限
List authorities = new ArrayList<>();
if (StringUtils.isNotBlank(user.getRoles())) {
String[] roles = user.getRoles().split(",");
for (String role : roles) {
if (StringUtils.isNotBlank(role)) {
authorities.add(new SimpleGrantedAuthority(role.trim()));
}
}
}
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), authorities);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
使用到的login.html、MD5Util和PasswordEncoder,代码如下:
login.html
Spring Security Example
Invalid username and password.
You have been logged out.
Click here go to home page.
MD5Util.java
package com.oycbest.util;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @Author: oyc
* @Date: 2018/12/3 11:11
* @Description: MD5加密工具
*/
public class MD5Util {
public static final int time = 5;
public static final String SALT = "springsecurity";
/**
* 密码加密方法
*
* @param password
* @return
*/
public static String encode(String password) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).");
}
try {
for (int i = 0; i < time; i++) {
byte[] bytes = digest.digest((password + SALT).getBytes("UTF-8"));
password = String.format("%032x", new BigInteger(1, bytes));
}
return password;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).");
}
}
public static void main(String[] args) {
System.out.println(MD5Util.encode("123456"));
}
}
PasswordEncoder.java
package com.oycbest.service;
import com.oycbest.util.MD5Util;
/**
* @Author: oyc
* @Date: 2018/12/3 10:29
* @Description: 密码加密类
*/
public class PasswordEncoder implements org.springframework.security.crypto.password.PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return MD5Util.encode((String) rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {//user Details Service验证
return encodedPassword.equals(MD5Util.encode((String) rawPassword));
}
}
先使用用户ouyang登录,因为ouayng拥有所有权限,所以可以访问admin和user;当使用admin用户登录的时候,可以正常访问到admin,但是访问user路径时候,就被拦截的,因为admin用户不具备访问user路径的权限。
UserService中的loadUserByUsername在认证过程获取数据如下:
源码地址:https://github.com/oycyqr/SpringSecurity