EE顛覆者第九章企業開發security

Spring Security 基於Spring項目的安全框架,充分利用依賴注入 和 Aop 來實現安全的功能。

安全框架有兩個重要的概念,

認證 Authentication :確認用戶可以訪問當前系統

和 授權 Authorization:確定用戶 在當前系統下所擁有的功能權限。

security的配置

  1. 提供了過濾器來實現功能,

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{
    }
  1. 配置

    和 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章