SpringSecurity是专门针对基于Spring项目的安全框架,充分利用了依赖注入和AOP来实现安全管控。在很多大型企业级系统中权限是最核心的部分,一个系统的好与坏全都在于权限管控是否灵活,是否颗粒化。在早期的SpringSecurity版本中我们需要大量的xml来进行配置,而基于SpringBoot整合SpringSecurity框架相对而言简直是重生了,简单到不可思议的地步。
SpringSecurity框架有两个概念认证和授权,认证可以访问系统的用户,而授权则是用户可以访问的资源,下面我们来简单讲解下SpringBoot对SpringSecurity安全框架的支持。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入druid最新maven依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- spring boot tomcat jsp 支持开启 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--servlet支持开启-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
初始化信息
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
u_id bigint(11) UNSIGNED not null AUTO_INCREMENT,
u_username varchar(50) ,
u_password varchar(255),
PRIMARY KEY (`u_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (
r_id bigint(11) UNSIGNED not null AUTO_INCREMENT,
r_name varchar(30),
PRIMARY KEY (`r_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
ur_id bigint(11) UNSIGNED not null AUTO_INCREMENT,
ur_user_id bigint(11),
ur_role_id bigint(11),
PRIMARY KEY (`ur_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT into users VALUES(1,'admin','123456');
INSERT into roles values(1,'超级管理员'),(2,'普通用户');
INSERT into user_roles VALUES(1,1,1),(2,1,2);
select * from users;
select * from roles;
select * from user_roles;
Model
RoleEntity:
@Entity
@Table(name = "roles")
public class RoleEntity implements Serializable
{
@Id
@Column(name = "r_id")
private Long id;
@Column(name = "r_name")
private String name;
@Column(name = "r_flag")
private String flag;
// get set 省略....
}
@Entity
@Table(name = "users")
public class UserEntity implements Serializable,UserDetails
{
@Id
@Column(name = "u_id")
private Long id;
@Column(name = "u_username")
private String username;
@Column(name = "u_password")
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles",
joinColumns = {
@JoinColumn(name = "ur_user_id")
},
inverseJoinColumns = {
@JoinColumn(name = "ur_role_id")
}
)
private List<RoleEntity> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<>();
List<RoleEntity> roles = getRoles();
for(RoleEntity role : roles)
{
auths.add(new SimpleGrantedAuthority(role.getFlag()));
}
return auths;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
// get set 省略......
}
Service
public class UserService implements UserDetailsService
{
@Autowired
UserJPA userJPA;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userJPA.findByUsername(username);
if(user == null)
{
throw new UsernameNotFoundException("未查询到用户:"+username+"信息!");
}
return user;
}
}
UserJPA:
public interface UserJPA extends JpaRepository<UserEntity,Long>
{
//使用SpringDataJPA方法定义查询
public UserEntity findByUsername(String username);
}
View
@Configuration
public class MVCConfig extends WebMvcConfigurerAdapter
{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/main").setViewName("main");
}
}
login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录界面</title>
</head>
<body>
<form action="/login" method="post">
<%--<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>--%>
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
main.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<sec:authorize access="hasRole('ROLE_ADMIN')">
您是超级管理员可以管理信息。
</sec:authorize>
<br/>
<sec:authorize access="hasRole('ROLE_USER')">
您是普通用户只能查看信息。
</sec:authorize>
</body>
</html>
WebSecurityConfig
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
//完成自定义认证实体注入
@Bean
UserDetailsService userService()
{
return new UserService();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()//所有请求必须登陆后访问
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()//登录界面,错误界面可以直接访问
.and()
.logout()
.permitAll();//注销请求可直接访问
}
}
spring config
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8
username: root
password: root
#最大活跃数
maxActive: 20
#初始化数量
initialSize: 1
#最大连接等待超时时间
maxWait: 60000
#打开PSCache,并且指定每个连接PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
#通过connectionProperties属性来打开mergeSql功能;慢SQL记录
#connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
filters: stat, wall, log4j
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp