Spring Boot整合Spring Security的示例代碼
本文講述Spring Boot整合Spring Security在方法上使用注解實現(xiàn)權(quán)限控制,使用自定義UserDetailService,從MySQL中加載用戶信息。使用Security自帶的MD5加密,對用戶密碼進(jìn)行加密。頁面模板采用thymeleaf引擎。
源碼地址:https://github.com/li5454yong/springboot-security.git
1、引入pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.15</version> </dependency> </dependencies>
這里使用druid連接池,Spring Data Jpa實現(xiàn)數(shù)據(jù)庫訪問。
2、配置Spring Security
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//開啟security注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//允許所有用戶訪問"/"和"/home"
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
//其他地址的訪問均需驗證權(quán)限
.anyRequest().authenticated()
.and()
.formLogin()
//指定登錄頁是"/login"
.loginPage("/login")
.defaultSuccessUrl("/hello")//登錄成功后默認(rèn)跳轉(zhuǎn)到"/hello"
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/home")//退出登錄后的默認(rèn)url是"/home"
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(customUserDetailsService())
.passwordEncoder(passwordEncoder());
}
/**
* 設(shè)置用戶密碼的加密方式為MD5加密
* @return
*/
@Bean
public Md5PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
/**
* 自定義UserDetailsService,從數(shù)據(jù)庫中讀取用戶信息
* @return
*/
@Bean
public CustomUserDetailsService customUserDetailsService(){
return new CustomUserDetailsService();
}
}
這里只做了基本的配置,設(shè)置了登錄url、登錄成功后跳轉(zhuǎn)的url、退出后跳轉(zhuǎn)到的url。使用@EnableGlobalMethodSecurity(prePostEnabled = true)這個注解,可以開啟security的注解,我們可以在需要控制權(quán)限的方法上面使用@PreAuthorize,@PreFilter這些注解。
3、自定義userDetailService
public class CustomUserDetailsService implements UserDetailsService {
@Autowired //數(shù)據(jù)庫服務(wù)類
private SUserService suserService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//SUser對應(yīng)數(shù)據(jù)庫中的用戶表,是最終存儲用戶和密碼的表,可自定義
//本例使用SUser中的email作為用戶名:
SUser user = suserService.findUserByEmail(userName);
if (user == null) {
throw new UsernameNotFoundException("UserName " + userName + " not found");
}
// SecurityUser實現(xiàn)UserDetails并將SUser的Email映射為username
SecurityUser securityUser = new SecurityUser(user);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return securityUser;
}
}
這里只需要實現(xiàn)UserDetailsService 接口,重寫loadUserByUsername方法,從數(shù)據(jù)庫中取出用戶信息。最后返回一個UserDetails 實現(xiàn)類。
4、定義錯誤處理配置
@Configuration
public class ErrorPageConfig {
@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new MyCustomizer();
}
private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403"));
}
}
}
訪問發(fā)生錯誤時,跳轉(zhuǎn)到”/403”.
5、Controller接口
@Controller
public class IndexController {
@Resource
private SUserService sUserService;
@RequestMapping("/home")
public String home() {
return "home";
}
@PreAuthorize("hasRole('user')")
@RequestMapping(value = "/admin",method = RequestMethod.GET)
public String toAdmin(){
return "helloAdmin";
}
@RequestMapping("/hello")
public String hello() {
return "hello";
}
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/")
public String root() {
return "index";
}
@RequestMapping("/403")
public String error(){
return "403";
}
}
在toAdmin()方法上面使用了@PreAuthorize(“hasRole(‘user')”),表示訪問這個方法需要擁有user角色。如果希望控制到權(quán)限層面,可以使用@PreAuthorize(“hasPermission()”)。這里只是用了其中的一個用法,更多的使用方法可以去看官方文檔。需要注意的是,Spring Security默認(rèn)的角色前綴是”ROLE_”,使用hasRole方法時已經(jīng)默認(rèn)加上了,因此我們在數(shù)據(jù)庫里面的用戶角色應(yīng)該是“ROLE_user”,在user前面加上”ROLE_”前綴。
6、測試
啟動項目,訪問http://localhost:1130/login

點擊登錄后進(jìn)入到“/hello”

點擊跳轉(zhuǎn)到管理員頁面

在后臺的“/admin”這個url對應(yīng)的方法上面,限制了用戶必須要擁有“user”角色。在數(shù)據(jù)庫中也設(shè)置了登錄的用戶有這個角色。
現(xiàn)在我們修改數(shù)據(jù)庫中的用戶角色,改為“ROLE_admin”。退出登錄后重新登錄,再次點擊“前往管理員頁面”按鈕,會跳轉(zhuǎn)到如下頁面。
因為現(xiàn)在沒有了“user”權(quán)限,所以訪問的時候拋出了異常,被攔截后重定向到了“/403”。
7、POST方式訪問,錯誤碼403
首先,把“/admin”改為POST請求
@PreAuthorize("hasRole('user')")
@RequestMapping(value = "/admin",method = RequestMethod.POST)
public String toAdmin(){
return "helloAdmin";
}
把“前往管理員頁面”按鈕的請求方式從原來的form表達(dá)get提交,改為ajax方式POST提交。至于為什么不是用form的POST提交后面再講。先修改代碼
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<!--<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
<form th:action="@{/admin}" method="get">
<input th:type="submit" th:value="前往管理員頁面"/>
</form>-->
<a th:href="@{/admin}" rel="external nofollow" >前往管理員用戶頁面</a>
<input th:type="submit" onclick="testPost()" th:value="前往管理員頁面"/>
</body>
<script>
function testPost() {
$.ajax({
url:"/admin",
type:'POST',
success:function (data) {
}
});
}
</script>
點擊“前往管理員頁面”按鈕,在調(diào)試臺可以看到如下
這是因為框架內(nèi)部防止CSRF(Cross-site request forgery跨站請求偽造)的發(fā)生,限制了除了get以外的大多數(shù)方法。
下面說解決辦法:
首先在標(biāo)簽內(nèi)添加如下內(nèi)容。
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_hader" th:content="${_csrf.headerName}"/>
只要添加這個token,后臺就會驗證這個token的正確性,如果正確,則接受post訪問。
然后在ajax代碼中添加以下代碼:
var token = $('meta[name="_csrf"]').attr("content");
var header = $('meta[name="_csrf_hader"]').attr("content");
$(document).ajaxSend(function(e,xhr,opt){
xhr.setRequestHeader(header,token);
});
這樣就可以正常使用POST、DELETE等其他方式來訪問了。
上面說到使用表單的POST方式來提交,通過查看頁面源代碼可以看到
框架在form表單中自動插入了一個隱藏域,value值就是那個token,所以使用form表單來提交POST請求是可以直接通過的,而ajax方式提交的話,需要加上那段代碼。
好了,這篇文章就講到這,后面還會有文章講述REST API風(fēng)格如何來使用Spring Security來控制權(quán)限。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringAOP切入點規(guī)范及獲取方法參數(shù)的實現(xiàn)
這篇文章主要介紹了SpringAOP切入點規(guī)范及獲取方法參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
淺談SpringBoot @Autowired的兩種注入方式
本文主要介紹了兩種SpringBoot @Autowired注入方式,具有一定的參考價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06
Failed to execute goal org...的解決辦法
這篇文章主要介紹了Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1的解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06
SecurityUtils.getSubject().getPrincipal()為null的問題
這篇文章主要介紹了SecurityUtils.getSubject().getPrincipal()為null的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
Java實現(xiàn)線程按序交替執(zhí)行的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java如何實現(xiàn)線程按序交替執(zhí)行,文中的示例代碼講解詳細(xì),對我們了解線程有一定幫助,需要的可以參考一下2022-10-10

