Spring Security是一個功能強大且高度可自訂的身份驗證和存取控制框架,Spring Security主要做兩個事情,認證、授權。我之前寫過一篇關於SpringSecurity的博客,但是當時只是介紹了基於mock數據的案例,本期就來介紹一下基於真實數據的認證授權實現。
為了更好的展示SpringSecurity,我們先建造一個簡單的web專案出來。引入thymeleaf依賴
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.thymeleaf</groupid> <artifactid>thymeleaf-spring5</artifactid> </dependency> <dependency> <groupid>org.thymeleaf.extras</groupid> <artifactid>thymeleaf-extras-java8time</artifactid> </dependency>
新建一個登陸頁,一個首頁,然後幾個不同等級的展示頁面: login.html
nbsp;html> <meta> <title>登陆页</title> <div> <form> <h3>登陆页</h3> <input> <input> <button>登陆</button> </form> </div>
index.html
nbsp;html> <meta> <title>首页</title> <div> <h3>首页</h3> <a>登陆</a> <div> <div> <h4>level1</h4> <a>level-1-1</a> <hr> <a>level-1-2</a> </div> <div> <h4>level2</h4> <a>level-2-1</a> <hr> <a>level-2-2</a> </div> <div> <h4>level3</h4> <a>level-3-1</a> <hr> <a>level-3-2</a> </div> </div> </div>
另外還有幾個不同等級的頁面
分別在body中寫上自己對應的編號。
nbsp;html> <meta> <title>Title</title> level-1-1
最後寫一個controller來接收請求:
@Controller public class RouteController { @RequestMapping({"/","/index"}) public String index(){ return "index"; } @RequestMapping("/login") public String toLogin(){ return "login"; } @RequestMapping("/level1/{id}") public String level1(@PathVariable("id")String id){ return "level1/"+id; } @RequestMapping("/level2/{id}") public String level2(@PathVariable("id")String id){ return "level2/"+id; } @RequestMapping("/level3/{id}") public String level3(@PathVariable("id")String id){ return "level3/"+id; } }
最終的效果如下:
最終實作等級不同的level頁面根據不同權限進行跳轉。
後台基於Mybatis和Mysql資料庫實現,因此我們除了引入SpringSecurity的依賴之外,還需要引入Mybatis相關依賴:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-security</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-jdbc</artifactid> </dependency> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <scope>runtime</scope> </dependency> <dependency> <groupid>org.mybatis.spring.boot</groupid> <artifactid>mybatis-spring-boot-starter</artifactid> <version>2.1.3</version> </dependency>
在設定文件中新增資料來源相關信息,以及Mybatis的配置:
spring.datasource.url=jdbc:mysql://localhost:3306/security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver mybatis.mapper-locations=classpath:mapper/*.xml
認證和授權在表設計上應該分在兩個表內,一個表存儲用戶信息包括密碼等,另一個表存儲授權信息,還需要一個表建立用戶和授權之間的關聯,給出最終的表結構:
CREATE TABLE `roles` ( `id` int(4) NOT NULL, `rolename` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `sysuser` ( `id` int(4) NOT NULL, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `user_role` ( `id` int(4) NOT NULL, `user_id` int(4) DEFAULT NULL, `role_id` int(4) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
接下來是針對這三張表的實體類,Mapper介面以及xml文件,你可以不看程式碼,主要實作一個透過使用者名稱來尋找使用者以及相關權限的操作:
@Data public class Roles { private Integer id; private String roleName; } @Data public class SysUser { private Integer id; private String userName; private String password; private List<roles> roles; }</roles>
Mapper介面:
public interface UserMapper { public SysUser getUserByUserName(@Param("userName") String userName); }
xml實作:
<?xml version="1.0" encoding="UTF-8" ?> nbsp;mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper> <resultmap> <id></id> <result></result> <result></result> <collection> <result></result> </collection> </resultmap> <select> select sysuser.*,roles.rolename from sysuser LEFT JOIN user_role on sysuser.id= user_role.user_id LEFT JOIN roles on user_role.role_id=roles.id where username= #{userName} </select> </mapper>
SpringSecurity的認證過程是這樣的,首先透過用戶名或其他唯一的ID在資料庫裡找到這個用戶,用戶的密碼以非對稱加密的方式儲存。取到用戶後將前台傳入的密碼加密後和資料庫中已經加密好的欄位進行對比,從而通過認證。
上面這個過程中的第一步透過使用者名稱找到使用者的操作需要透過Service服務來實現,而這個Service服務需要繼承SpringSecurity中的UserDetailsService介面。這個介面回傳一個SpringSecurity的User物件。
@Service public class UserService implements UserDetailsService { @Resource private UserMapper userMapper; //根据用户名找到对应的用户信息 @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { SysUser sysUser = userMapper.getUserByUserName(s); if (sysUser!=null){ List<grantedauthority> roles=new ArrayList(); sysUser.getRoles().stream().forEach(x->{ roles.add(new SimpleGrantedAuthority(x.getRoleName())); }); return new User(sysUser.getUserName(),sysUser.getPassword(),roles); } throw new UsernameNotFoundException("用户未找到"); } }</grantedauthority>
上面的步驟都完成後就開始設定Security了,寫一個設定方法SecurityConfig,程式碼層面很簡單,認證傳入userService對象,會自動把資料庫中取出的密碼和前端傳過來的密碼進行對照。同時在userService中也傳入了roles集合,在授權處為不同的頁面附上不同的權限即可。
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; //授权 @Override protected void configure(HttpSecurity http) throws Exception { //首页所有人都能访问,level页面只有有权限的人才能访问 http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); //没有权限默认跳到登陆页,默认会重定向到/login http.formLogin(); } //认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder()); } }
我在認證的時候使用的密碼加密方式是BCryptPasswordEncoder,因此存入資料庫中的密碼也需要被加密,常用的方式就是在註冊時通過同樣的方式將密碼加密存入資料庫:
String password="xxx"; BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); String encode=bCryptPasswordEncoder.encode(password);
以上是如何用SpringBoot+SpringSecurity實現基於真實資料的授權認證的詳細內容。更多資訊請關注PHP中文網其他相關文章!