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