您好,登錄后才能下訂單哦!
本文講述Spring Boot整合Spring Security在方法上使用注解實現權限控制,使用自定義UserDetailService,從MySQL中加載用戶信息。使用Security自帶的MD5加密,對用戶密碼進行加密。頁面模板采用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-oauth3</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實現數據庫訪問。
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() //其他地址的訪問均需驗證權限 .anyRequest().authenticated() .and() .formLogin() //指定登錄頁是"/login" .loginPage("/login") .defaultSuccessUrl("/hello")//登錄成功后默認跳轉到"/hello" .permitAll() .and() .logout() .logoutSuccessUrl("/home")//退出登錄后的默認url是"/home" .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(customUserDetailsService()) .passwordEncoder(passwordEncoder()); } /** * 設置用戶密碼的加密方式為MD5加密 * @return */ @Bean public Md5PasswordEncoder passwordEncoder() { return new Md5PasswordEncoder(); } /** * 自定義UserDetailsService,從數據庫中讀取用戶信息 * @return */ @Bean public CustomUserDetailsService customUserDetailsService(){ return new CustomUserDetailsService(); } }
這里只做了基本的配置,設置了登錄url、登錄成功后跳轉的url、退出后跳轉到的url。使用@EnableGlobalMethodSecurity(prePostEnabled = true)這個注解,可以開啟security的注解,我們可以在需要控制權限的方法上面使用@PreAuthorize,@PreFilter這些注解。
3、自定義userDetailService
public class CustomUserDetailsService implements UserDetailsService { @Autowired //數據庫服務類 private SUserService suserService; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { //SUser對應數據庫中的用戶表,是最終存儲用戶和密碼的表,可自定義 //本例使用SUser中的email作為用戶名: SUser user = suserService.findUserByEmail(userName); if (user == null) { throw new UsernameNotFoundException("UserName " + userName + " not found"); } // SecurityUser實現UserDetails并將SUser的Email映射為username SecurityUser securityUser = new SecurityUser(user); Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); return securityUser; } }
這里只需要實現UserDetailsService 接口,重寫loadUserByUsername方法,從數據庫中取出用戶信息。最后返回一個UserDetails 實現類。
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")); } } }
訪問發生錯誤時,跳轉到”/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角色。如果希望控制到權限層面,可以使用@PreAuthorize(“hasPermission()”)。這里只是用了其中的一個用法,更多的使用方法可以去看官方文檔。需要注意的是,Spring Security默認的角色前綴是”ROLE_”,使用hasRole方法時已經默認加上了,因此我們在數據庫里面的用戶角色應該是“ROLE_user”,在user前面加上”ROLE_”前綴。
6、測試
啟動項目,訪問http://localhost:1130/login
點擊登錄后進入到“/hello”
點擊跳轉到管理員頁面
在后臺的“/admin”這個url對應的方法上面,限制了用戶必須要擁有“user”角色。在數據庫中也設置了登錄的用戶有這個角色。
現在我們修改數據庫中的用戶角色,改為“ROLE_admin”。退出登錄后重新登錄,再次點擊“前往管理員頁面”按鈕,會跳轉到如下頁面。
因為現在沒有了“user”權限,所以訪問的時候拋出了異常,被攔截后重定向到了“/403”。
7、POST方式訪問,錯誤碼403
首先,把“/admin”改為POST請求
@PreAuthorize("hasRole('user')") @RequestMapping(value = "/admin",method = RequestMethod.POST) public String toAdmin(){ return "helloAdmin"; }
把“前往管理員頁面”按鈕的請求方式從原來的form表達get提交,改為ajax方式POST提交。至于為什么不是用form的POST提交后面再講。先修改代碼
<body> <h2 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h2> <!--<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>
點擊“前往管理員頁面”按鈕,在調試臺可以看到如下
這是因為框架內部防止CSRF(Cross-site request forgery跨站請求偽造)的發生,限制了除了get以外的大多數方法。
下面說解決辦法:
首先在標簽內添加如下內容。
<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風格如何來使用Spring Security來控制權限。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。