Spring Security 基于Spring项目的安全框架,充分利用依赖注入 和 Aop 来实现安全的功能。
安全框架有两个重要的概念,
认证 Authentication :确认用户可以访问当前系统
和 授权 Authorization:确定用户 在当前系统下所拥有的功能权限。
security的配置
- 提供了过滤器来实现功能,
Dele gating Filter Proxy 过滤器到 Web Application Initializer
实际使用 让自己的 Initializer 类继承 Abstract Security Web Application Initializer 抽象类即可。(实现了 web…lizer接口),并通过 on Start up方法调用。
insert Spring Security Filter Chain (servletContext);
它为我们注册了, DelegatingFilterProxy.
代码:
public class AppInitializer extends AbstractSecurityWebApplicationInitializer{
}
-
配置
和 MVC配置类似
@Configuration @EnableWebSecurity //默认开启 public class WebSecurityConfig extends WebSecurityConfigurerAdapter{//1 继承配置 //重写configure方法 }
@Configuration //MVC配置如下
public class WebMvcConfig extends WebMvcConfigurerAdapter{
用户认证
认证需要一套数据的来源,
授权对于某个用户有相应的角色权限。
@Override
protected void configure(AuthenticationManagerBuilder auth)
内存认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("lisi").password("123").roles("ROLE_ADMIN")
.and()
.withUser("wangwu").password("123").roles("ROLE_USER");
}
jdbc认证
@Resource
DataSource dataSource;
auth.jdbcAuthentication().dataSource(dataSource);
对应JdbcDaoImpl
"select username,password,enabled from users where username = ?";
"select username,authority from authorities where username = ?";
当然我们也可以自定义
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"select username,password,true from myusers where username = ?")
.authoritiesByUsernameQuery(
"select username,role from roles where username = ?");
普通的用户
出了内存和Jdbc,我们要自定义。 实现UserDetailService
@Bean
UserDetailsService customUserService(){ //2
return new CustomUserService();
}
auth.userDetailsService(customUserService()); //3
//详见实例
CustomUserService implements UserDetailsService
请求授权
@Override
protected void configure(HttpSecurity http) throws Exception {
}
匹配器包含:
antMatchers 使用ant 风格匹配路径
regexMatchers 使用正则表达式匹配路径
anyRequest 匹配所有路径
匹配了请求路径后,针对当前用户的信息对请求路径进行安全处理,提供弄了如下的安全方法
方法 | 用途 |
---|---|
access(String) | Spring EL表达式结果为true时可访问 |
anonymous() | 匿名可访问 |
denyAll() | 拒绝所有 |
fully Authenticated() | 用户完全认证可访问(不是记住我 下的自动登录) |
has Any Authority(String …) | 如果用户有参数,则其中任一权限 可访问 |
has Any Role(String …) | 如果用户有参数,则其中任一角色 可访问 |
has Authority(String …) | 如果用户有参数,则其权限可访问 |
has IpAddress(String …) | 如果用户来自参数中的IP可访问 |
has Role(String) | 有参数中的角色可访问 |
permitAll() | 用户可任一访问 |
rememberMe() | 允许 记住我 登录的用户访问 |
authenticated() | 用户登录后可访问 |
http.authorizeRequests() //开始请求权限配置
.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
.antMatchers("/user/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")
.anyRequest().authenticated();
//admin下请求要有 admin的权限
//user下请求,要有user 或者 admin的权限
//其余的请求,都要登录
http.
formLogin().loginPage("/login").defaultSuccessUrl("/index").failureUrl("/login?error").permitAll()
.and()
.rememberMe().tokenValiditySeconds(1209600).key("myKey")
.and()
.logout().logoutUrl("/custom-logout").logoutSuccessUrl("/logout-success").permitAll();
//formLogin定制登录
//rememberMe开户cookie存储用户信息,1209600秒,就是2周,key就是 cookie中的私钥
//logoutUrl注销URL路径
spring boot支持
在 autoconfigure.security包中
@Configuration
@ConditionalOnClass({ AuthenticationManager.class,
GlobalAuthenticationConfigurerAdapter.class })
@EnableConfigurationProperties
@Import({ SpringBootWebSecurityConfiguration.class,
AuthenticationManagerConfiguration.class,
BootGlobalAuthenticationConfiguration.class })
public class SecurityAutoConfiguration {
}
//导入了SpringBootWebSecurityConfiguration
public class SpringBootWebSecurityConfiguration {
private static List<String> DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**",
"/images/**", "/**/favicon.ico");
//1,自动配置了一个用户,账号为user,密码在程序启动时候出现
//忽略上面的文件夹
//自动配置的securityFilterChainRegistration的bean
}
@ConfigurationProperties(prefix = "security")//security开头
public class SecurityProperties implements SecurityPrerequisite {
}
配置
security:
user:
name: user
password: 123
role: USER
require-ssl: false #是否需要SSL的支持
enable-csrf: false #是否开启 跨域请求伪造 支持,默认关闭
basic:
enabled: true
realm: Spring
path: /**
authorize-mode: authenticated #必须是一个认证的用户,默认不知道啥
filter-order: 0
headers:
xss: false
cache: false
frame: false
content-type: false
hsts: all
sessions: stateless
ignored: #用逗号隔开 无需拦截的路径
@Configuration //自己扩展用
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{//1
}
实战
Pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
配置文件
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot
logging.level.org.springframework.security= INFO
spring.thymeleaf.cache=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
静态文件 和主类如下
src/main/resources/static/css/bootstrap.min.css
@SpringBootApplication
public class Ch91Application {
public static void main(String[] args) {
SpringApplication.run(Ch91Application.class, args);
}
}
用户和角色
@Entity
public class SysUser implements UserDetails{ //1 实现这个接口,即为Security所使用的用户
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
//配置用户和角色的对应关系
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
private List<SysRole> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() { //2 将用户的角色作为权限
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
List<SysRole> roles=this.getRoles();
for(SysRole role:roles){
auths.add(new SimpleGrantedAuthority(role.getName()));
}
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;
}
}
@Entity
public class SysRole {
@Id
@GeneratedValue
private Long id;
private String name;
}
初始化 sql
spring.jpa.hibernate.ddl-auto=update
系统会自动生成 SYS_USER , SYS_ROLE 和 SYS_USER_ROLES
src/main/resources/data.sql
insert into SYS_USER (id,username, password) values (1,'wyf', 'wyf'); //管理员
insert into SYS_USER (id,username, password) values (2,'wisely', 'wisely'); //用户
insert into SYS_ROLE(id,name) values(1,'ROLE_ADMIN');
insert into SYS_ROLE(id,name) values(2,'ROLE_USER');
insert into SYS_USER_ROLES(SYS_USER_ID,ROLES_ID) values(1,1);
insert into SYS_USER_ROLES(SYS_USER_ID,ROLES_ID) values(2,2);
传值对象
public class Msg {
private String title;
private String content;
private String etraInfo;
public Msg(String title, String content, String etraInfo) {
super();
this.title = title;
this.content = content;
this.etraInfo = etraInfo;
}
}
数据访问
public interface SysUserRepository extends JpaRepository<SysUser, Long>{
SysUser findByUsername(String username);
}
自定义 UserDetailsService
public class CustomUserService implements UserDetailsService { //1
@Autowired
SysUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) { //2
SysUser user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
return user; //3
}
}
与这些对应
@Bean
UserDetailsService customUserService(){ //2
return new CustomUserService();
}
@Override //配置认证的
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()); //3
}
MVC配置
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
} // /login跳转到login页面
}
spring Security配置
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{//1 集成
@Bean
UserDetailsService customUserService(){ //2
return new CustomUserService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()); //3
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() //4 所有的请求登录后才能访问
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.permitAll() //5定制登录,登录页面可 任意访问
.and()
.logout().permitAll(); //6 注销页面 可 任意访问
}
}
@Order(100)
public abstract class WebSecurityConfigurerAdapter{} //集成抽象类
页面
src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录页面</title>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Spring Security演示</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a th:href="@{/}"> 首页 </a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div class="starter-template">
<p th:if="${param.logout}" class="bg-warning">已成功注销</p><!-- 1 -->
<p th:if="${param.error}" class="bg-danger">有错误,请重试</p> <!-- 2 -->
<h2>使用账号密码登录</h2>
<form name="form" th:action="@{/login}" action="/login" method="POST"> <!-- 3 -->
<div class="form-group">
<label for="username">账号</label>
<input type="text" class="form-control" name="username" value="" placeholder="账号" />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" name="password" placeholder="密码" />
</div>
<input type="submit" id="login" value="Login" class="btn btn-primary" />
</form>
</div>
</div>
</body>
</html>
src/main/resources/templates/home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"><!-- 1 -->
<head>
<meta content="text/html;charset=UTF-8"/>
<title sec:authentication="name"></title> <!-- 2 --> 获得到当前用户的用户名
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}" />
<style type="text/css">
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Spring Security演示</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a th:href="@{/}"> 首页 </a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div class="starter-template">
<h1 th:text="${msg.title}"></h1>
<p class="bg-primary" th:text="${msg.content}"></p>
<div sec:authorize="hasRole('ROLE_ADMIN')"> <!-- 3 --> 只有当前用户是管理员 才显示
<p class="bg-info" th:text="${msg.etraInfo}"></p>
</div>
<div sec:authorize="hasRole('ROLE_USER')"> <!-- 4--> 只有当前用户是用户才展示
<p class="bg-info">无更多信息显示</p>
</div>
<form th:action="@{/logout}" method="post">
<input type="submit" class="btn btn-primary" value="注销"/><!-- 5 -->
</form>
</div>
</div>
</body>
</html>
控制器
@Controller
public class HomeController {
@RequestMapping("/")
public String index(Model model){
Msg msg = new Msg("测试标题","测试内容","额外信息,只对管理员显示");
model.addAttribute("msg", msg);
return "home";
}
}
http://localhost:8080/ wyf