SpringSecurity頁面授權與登錄驗證實現(內存取值與數據庫取值)
SpringSecurity?
Spring Security是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供瞭一組可以在Spring應用上下文中配置的Bean,充分利用瞭Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴註入)和AOP(面向切面編程)功能,為應用系統提供聲明式的安全訪問控制功能,減少瞭為企業系統安全控制編寫大量重復代碼的工作
絕大部分對於項目的說明寫在代碼註釋中
此博客中的項目基於SpringBoot(2.6.7)整合Mybatis項目創建,其中大部分依賴版本依據SpringBoot(2.6.7)而定,小部分官方未提供版本建議需自行指定
一.導入依賴
<dependencies> <!--security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!--devtools--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
二.配置yml文件
server: port: 8080 mybatis: mapper-locations: classpath:mappers/*.xml configuration: map-underscore-to-camel-case: true spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 123456789 #德魯伊數據源 type: com.alibaba.druid.pool.DruidDataSource druid: #SpringBoot默認是不註入 需要自己綁定至bean(使用java配置bean時 因為springboot內置瞭servlet容器 所以無web.xml 需要@Bean將配置註入) #druid數據源專有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置監控統計攔截的filters。stat:監控統計、wall:防禦sql註入、log4j:日志記錄 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #配置 DruidStatFilter web-stat-filter: enabled: true url-pattern: /* exclusions: /druid/*,*.js,*.css,*.gif,*.jpg,*.bmp,*.png,*.ico #配置 DruidStatViewServlet stat-view-servlet: #訪問德魯伊監控頁面的地址 url-pattern: /druid/* #IP白名單 沒有配置或者為空 則允許所有訪問 allow: 127.0.0.1 #IP黑名單 若白名單也存在 則優先使用 # deny: ip地址 #禁用重置按鈕 # reset-enable: false #登錄德魯伊監控頁面所用的用戶名與密碼 login-username: root login-password: 123456 #關閉thymeleaf緩存 修改代碼後無需重啟即可更新 thymeleaf: cache: false #security認證設置 配置類中若存在設置 則yml文件不生效 # security: # user: # name: # password: # roles:
三.代碼部分
DAO層(註意@Repository與@Mapper註解)
@Repository @Mapper public interface AuthUserMapper { AuthUser queryByUserName(String username); List<AuthRole> queryRoleByUserId(int id); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.alan.springboot.DAO.AuthUserMapper"> <select id="queryByUserName" parameterType="String" resultType="cn.alan.springboot.POJO.AuthUser"> select * from mybatis.auth_user where username = #{username}; </select> <select id="queryRoleByUserId" parameterType="int" resultType="cn.alan.springboot.POJO.AuthRole"> select * from mybatis.auth_role where user_id = #{id}; </select> </mapper>
Service層(註意@Service註解)
Service類需要實現UserDetailsService接口
@Service public class AuthUserService implements UserDetailsService { @Autowired AuthUserMapper authUserMapper; //根據用戶名從數據庫獲取用戶信息 密碼驗證由SpringSecutit進行 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { AuthUser user = authUserMapper.queryByUserName(username); if (user == null) { throw new UsernameNotFoundException("unknown username");//拋出異常 } List<AuthRole> authRole = authUserMapper.queryRoleByUserId(user.getId()); user.setAuthRoles(authRole); return user; } }
Controller層(註意@Controller註解)
@Controller//springsecurity controller類 public class RouterController { @Autowired cn.alan.springboot.DAO.AuthUserMapper authUserMapper; @RequestMapping({"/", "/index"}) public String toIndex(){ return "index"; } @RequestMapping("/toLogin") public String toLogin(){ return "security/login"; } @RequestMapping("/add") public String toAdd(){ return "security/add"; } @RequestMapping("/update") public String toUpdate(){ return "security/update"; } @RequestMapping("/admin") public String toAdmin(){ return "security/admin"; } }
POJO
User實體類需要實現UserDetails接口
@Data @Getter @Setter //必須實現所有UserDetails方法 必須有與方法對應的變量 數據庫中是否有對應字段不影響驗證 public class AuthUser implements UserDetails { private int id; private String username; private String password; private List<AuthRole> AuthRoles; private int accountNonExpired; private int accountNonLocked; private int credentialsNonExpired; private int enabled; //獲取用戶所有角色信息 @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for(AuthRole role : AuthRoles){ //為所有角色字段的數據加上 ROLE_ 前綴 //無此前綴無法被security識別為角色 //當然 可以在數據庫中直接添加前綴 authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getUserRole())); } return authorities; } //判斷賬戶是否過期 @Override public boolean isAccountNonExpired() { return accountNonExpired==0; } //判斷賬戶是否鎖定 @Override public boolean isAccountNonLocked() { return accountNonLocked==0; } //判斷密碼是否過期 @Override public boolean isCredentialsNonExpired() { return credentialsNonExpired==0; } //判斷賬戶是否可用 @Override public boolean isEnabled() { return enabled==0; } }
@Data @Getter @Setter public class AuthRole { int userId; String userRole; }
Config
Config類需要繼承WebSecurityConfigurerAdapter類,且需要添加一個加密Bean
package cn.alan.springboot.Config; import cn.alan.springboot.Service.AuthUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity//開啟springsecurity //繼承WebSecurityConfigurerAdapter類 public class SecurityConfig extends WebSecurityConfigurerAdapter { //授權 @Override protected void configure(HttpSecurity http) throws Exception { //規則 http.authorizeHttpRequests() .antMatchers("/").permitAll()//該頁面允許所有人訪問 .antMatchers("/add").hasRole("add")//訪問/level1/* 需要權限1 .antMatchers("/update").hasRole("update")//訪問/level2/* 需要權限2 .antMatchers("/admin").hasRole("admin");//訪問/level3/* 需要權限3 //開啟註銷功能(默認使用security提供的註銷頁面) //.logoutSuccessUrl() 註銷成功後返回的頁面 http.logout().logoutSuccessUrl("/"); //沒有權限會默認走登錄頁面(默認使用security提供的登陸頁面) //.loginPage() 自定義登陸頁面 //.loginProcessingUrl() 設置實際處理提交的頁面 設置後將登陸頁面中表單的action設置為相同url //.usernameParameter() 表單中username輸入框的name屬性 默認為username //.passwordParameter() 表單中password輸入框的name屬性 默認為password http.formLogin().loginPage("/toLogin"); //註:自定義登陸頁面後security提供的登錄頁面將失效 同時security提供的註銷詢問頁面也將失效 但註銷功能任然可用 //開啟"remember me"功能 //.rememberMeParameter() 表單中rememberme單選框的name屬性 默認為remember-me http.rememberMe(); //關閉CSRF防護 //自定義登陸頁面後 不關閉此選項將無法註銷 http.csrf().disable(); } @Autowired AuthUserService authUserService; //認證 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(authUserService); //從內存中取值 在新版本中需要對密碼進行加密 否則無法登陸 .passwordEncoder(new BCryptPasswordEncoder()) // auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) // .withUser("one").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1") // .and()//該方法中使用鏈式寫法配置用戶信息 用戶之間需要用.and()連接 // .withUser("two").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2") // .and() // .withUser("three").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3"); } //加密 前端的明文密碼會在被加密後與後端數據庫中的密文進行比對 //SpringSecurity默認開啟加密 數據庫中的密碼若不符合密文格式 認證不會通過 //記得@Bean @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
Utils
加密工具類,此處采用BCryptPasswordEncoder進行加密
public class PasswordEncoderUtils { private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Test public void encode() { String password = "123"; String encodedPassword = passwordEncoder.encode(password); System.out.println("password: " + password); System.out.println("encodedPassword: " + encodedPassword); } }
數據庫
資源目錄結構
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" > <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <center> <h1>index</h1> </center> <hr> <center> <div sec:authorize="!isAuthenticated()"><!--未認證時顯示--> <a th:href="@{/toLogin}" rel="external nofollow" ><h1>Login</h1></a> </div> <div sec:authorize="isAuthenticated()"><!--認證後時顯示--> <!--註銷--> <!--security自帶logout頁面--> <a th:href="@{/logout}" rel="external nofollow" > <h1>Logout</h1> </a> </div> <div sec:authorize="hasRole('add')"><!--擁有add權限時顯示--> <a th:href="@{/add}" rel="external nofollow" ><h1>add</h1></a> </div> <div sec:authorize="hasRole('update')"><!--擁有update權限時顯示--> <a th:href="@{/update}" rel="external nofollow" ><h1>update</h1></a> </div> <div sec:authorize="hasRole('admin')"><!--擁有admin權限時顯示--> <a th:href="@{/admin}" rel="external nofollow" ><h1>admin</h1></a> </div> <hr> <div sec:authorize="isAuthenticated()"><!--認證後時顯示--> <strong>用戶名:</strong><strong sec:authentication="name"></strong> <br> <strong>角色:</strong><span sec:authentication="principal.authorities"></span> </div> </center> </body> </html>
login.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>登錄</title> </head> <body> <div style="text-align: center"> <h1>Login</h1> </div> <div style="text-align: center"> <form th:action="@{/toLogin}" method="post"> <strong>Username</strong> <br> <!--賬戶與密碼的name在security中默認值為username與password--> <!--若需自定義 可在security的config類中自定義--> <input type="text" name="username"> <br> <br> <strong>Password</strong> <br> <input type="password" name="password"> <br> <br> <div> <input type="checkbox" name="remember-me"><strong>remember me</strong> </div> <br> <br> <!--註意type需為submit 勿錯寫為button--> <input type="submit" value="Submit"> </form> </div> </body> </html>
add.html(update.html、admin.html與此大同小異,不贅述)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div style="text-align: center"> <h1>add</h1> </div> </body> </html>
運行效果
訪問localhost:8080進入首頁,點擊登錄按鈕進入登錄頁面
輸入數據庫中的賬戶密碼(未加密),點擊提交按鈕進行登錄
不同的賬戶擁有的角色不同,首頁顯示的內容也不盡相同。可點擊註銷按鈕進行註銷
註銷後返回首頁
至此,頁面授權與登錄認證(數據庫取值)均完成。到此這篇關於SpringSecurity頁面授權與登錄驗證實現(內存取值與數據庫取值)的文章就介紹到這瞭,更多相關SpringSecurity頁面授權與登錄驗證內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot整合BCrypt實現密碼加密
- java SpringSecurity使用詳解
- 一文詳解Spring Security的基本用法
- SpringBoot+SpringSecurity實現基於真實數據的授權認證
- 關於SpringSecurity配置403權限訪問頁面的完整代碼