- 首先是使用的SpringBoot框架
基礎需要的pom以來如下,基礎的springboot項目的創建就不一一贅述了。
<!--spring web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--jpa 對數據庫操作的框架 類似mybatis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--頁面引擎 thymeleaf模板--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--mysql數據庫驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
- 加入必須的security依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
數據庫連接配置文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/springsecurity?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=user spring.datasource.password=password
#設置運行時打印sql語句 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update #關閉thymeleaf緩存 spring.thymeleaf.cache=false
#不用數據庫認證時默認的用戶名和密碼
#spring.security.user.name=admin
#spring.security.user.password=1234
- 實體類***********重要***********
對於實體類的創建:
- 用戶表包含權限表
- 用戶表不包含權限表
筆者使用的是用戶表包含權限表的方式,那么不包含權限表該怎么解決呢?
創建一個新的實體類,如SecurityUser 繼承User 實現UserDetails 它包含User的內容和UserDetails的內容,對於權限的設置就不一樣了
重點注意這個方法 getAuthorities ,它來將權限交給security管理 暫時可以用Null值 在UserDetailsService中具體賦值
@Entity public class User implements Serializable, UserDetails { private String username; private String password; private String role; public User(){} public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } /** * 指示用戶的賬戶是否已過期。無法驗證過期的賬戶。 * 如果用戶的賬戶有效(即未過期),則返回true,如果不在有效就返回false */ @Override public boolean isAccountNonExpired() { return true; } /** * 指示用戶是鎖定還是解鎖。無法對鎖定的用戶進行身份驗證。 * 如果用戶未被鎖定,則返回true,否則返回false */ @Override public boolean isAccountNonLocked() { return true; } /** * 指示用戶的憑證(密碼)是否已過期。過期的憑證阻止身份驗證 * 如果用戶的憑證有效(即未過期),則返回true * 如果不在有效(即過期),則返回false */ @Override public boolean isCredentialsNonExpired() { return true; } /** * 指示用戶是啟用還是禁用。無法對禁用的用戶進行身份驗證 * 如果啟用了用戶,則返回true,否則返回false */ @Override public boolean isEnabled() { return true; } /** * 得到用戶的權限,如果權限表和用戶表是分開的,我們需要在重新定義一個實體類實現UserDetails 並且繼承於User類
* 交給security的權限,放在UserDetailService進行處理 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); //角色必須以ROLE_開頭,如果數據庫沒有,則需要加上 至於角色為什么必須要以ROLE_開頭 筆者沒有進行深究 authorities.add(new SimpleGrantedAuthority("ROLE_"+this.role)); return authorities; } }
- 此處就先不談security的config配置吧,后面會提到
我們還沒有用到數據庫中的數據,在此我們需要改造UserServiceImpl類,讓其實現 UserDetailsService 並重寫其中的 loadUserByUsername 方法,這是數據庫認證的必要流程,貼代碼:
@Service public class UserServiceImpl implements UserService, UserDetailsService { @Autowired UserDao userDao; /** * 實現security認證實現的方法 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.error("username:" + username); //option這個東西請讀者自行研究,此處可以直接是一個user,optional只是用於判斷空值,避開空指針異常的 Optional<User> byUsername = userDao.findByUsername(username); if (!byUsername.isPresent()) { throw new UsernameNotFoundException("用戶名不存在,請先注冊后登錄!"); }else{ //權限表和用戶表分開************按照下面方式,如果不是,直接返回帶有權限信息的User對象 //查詢的權限在此處可以通過數據庫查詢,並且賦值 //List<GrantedAuthority> authorities=new ArrayList<>(); //authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER")) //新創建一個SecurityUser(自定義實現了UserDetails的類) //將authorites放到該對象中,並返回出去 return byUsername.get(); } } }
- 對於security的配置******************重要
新創建一個配置類繼承 WebSecurityConfigurerAdapter
需要注意的幾個方法(重寫的方法):
configure(HttpSecurity http) : htpp請求安全處理
configure(AuthenticationManagerBuilder auth) : 自定義數據庫權限認證
configure(WebSecurity web) : WEB安全
@Configuration @EnableWebSecurity // 開啟注解 @EnableGlobalMethodSecurity(prePostEnabled = true) // 開啟注解 //自定義了一個springSecurity安全框架的配置類 繼承WebSecurityConfigurerAdapter,重寫其中的方法configure, public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserServiceImpl userService; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() //接觸防止跨域請求 .authorizeRequests() .antMatchers("/toLogin","/templates/**","/logout","/toIndex").permitAll() //忽略認證的url .anyRequest().authenticated() //其他的url都需要被認證才可以訪問 .and() .formLogin() //允許自定義表單登錄 .loginPage("/toLogin") //這是action地址 不能寫具體頁面 確定自定義登錄頁 自己不需要寫login方法 登錄是由security提供 .loginProcessingUrl("/login") //這是html中form中action的值 必須要對應 .defaultSuccessUrl("/toIndex") //默認登錄成功會跳轉的controller //關於登錄失敗的提示信息,請讀者自行解決 .failureForwardUrl("/login-error") .failureUrl("/login-error"); } //密碼加密,最新的security必須使用密碼加密 @Bean public PasswordEncoder passwordEncoder(){ //使用BCcypt加密 return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //從數據庫讀取的用戶進行身份認證 實現了userDetailsService的類 auth.userDetailsService(userService) .passwordEncoder(passwordEncoder()); } //解除對靜態資源的保護 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/js/**","/templates/**"); } }
- 接下來是Controller和頁面
- 必須要登錄成功才可以訪問其他的接口,沒有通過認證會一直卡在login
@Controller public class UserController { @Autowired UserServiceImpl userService; @RequestMapping("/toIndex") public String index(){ return "index"; } @RequestMapping("/toLogin") public String login(){ return "login"; } //定義需要訪問的權限 @PreAuthorize("hasAnyRole('ROLE_admin')") @RequestMapping("/sayHello") @ResponseBody public String sayHello(){ return "hello"; } //定義需要訪問的權限 @PreAuthorize("hasAnyAuthority('ROLE_user')") @RequestMapping("/sayYes") @ResponseBody public String sayYes(){ return "yes"; } }
html頁面
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!--layui css--> <link rel="stylesheet" th:href="@{js/layui-v2.5.5/layui/css/layui.css}"> <!--layui js--> <script th:src="@{js/layui-v2.5.5/layui/layui.js}"></script> <!--jquery--> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <title>登錄頁面</title> </head> <body> <!--這里請注意action的值需要與loginProcessingUrl 相對應--> <form action="/login" method="post"> <div class="layui-form-item"> <label class="layui-form-label">輸入框</label> <div class="layui-input-inline"> <input type="text" id="username" name="username" required lay-verify="required" placeholder="請輸入用戶名" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">密碼框</label> <div class="layui-input-inline"> <input type="password" name="password" required lay-verify="required" placeholder="請輸入密碼" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button class="layui-btn layui-btn-warm" lay-submit lay-filter="formDemo">登錄</button> </div> </div> </form> </body> </html>
第一次寫博客 感覺還是有點亂,請和諧討論,筆者也是看了很多其他的博客慢慢弄出來的,希望大家不要害怕麻煩,慢慢來,引入就不貼了,看了太多 也不知道誰是誰的