<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
使用者表、角色表、使用者角色關聯表
建表語句
CREATE TABLE `role` ( `id` int(11) NOT NULL, `name` varchar(32) DEFAULT NULL, `nameZh` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `user` ( `id` int(11) NOT NULL, `username` varchar(32) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `enabled` varchar(1) DEFAULT NULL, `locked` varchar(1) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `user_role` ( `id` int(11) NOT NULL, `uid` int(11) DEFAULT NULL, `rid` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
初始化資料
注意:角色名有一個預設的字首 ROLE_
INSERT INTO `user`(`id`, `username`, `password`, `enabled`, `locked`) VALUES (1, 'root', '$2a$10$x6CBW1qnQqKPIxUSefZN7ebfTEiYNnfzzVjPJzlRhg5XyMzSWoO4e', '1', '0'); INSERT INTO `user`(`id`, `username`, `password`, `enabled`, `locked`) VALUES (2, 'admin', '$2a$10$x6CBW1qnQqKPIxUSefZN7ebfTEiYNnfzzVjPJzlRhg5XyMzSWoO4e', '1', '0'); INSERT INTO `user`(`id`, `username`, `password`, `enabled`, `locked`) VALUES (3, 'tangsan', '$2a$10$x6CBW1qnQqKPIxUSefZN7ebfTEiYNnfzzVjPJzlRhg5XyMzSWoO4e', '1', '0'); INSERT INTO `role`(`id`, `name`, `nameZh`) VALUES (1, 'ROLE_dba', '資料庫管理員'); INSERT INTO `role`(`id`, `name`, `nameZh`) VALUES (2, 'ROLE_admin', '系統管理員'); INSERT INTO `role`(`id`, `name`, `nameZh`) VALUES (3, 'ROLE_user', '普通使用者'); INSERT INTO `user_role`(`id`, `uid`, `rid`) VALUES (1, 1, 1); INSERT INTO `user_role`(`id`, `uid`, `rid`) VALUES (2, 1, 2); INSERT INTO `user_role`(`id`, `uid`, `rid`) VALUES (3, 2, 2); INSERT INTO `user_role`(`id`, `uid`, `rid`) VALUES (4, 3, 3);
MyBatis 靈活,JPA 便利,此處選擇前者,建立 Spring Boot Web 專案新增如下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
在 application.properties 中進行資料連線設定
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/jpa
Role
public class Role { private Integer id; private String name; private String nameZh; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNameZh() { return nameZh; } public void setNameZh(String nameZh) { this.nameZh = nameZh; } }
User
public class User implements UserDetails { private Integer id; private String username; private String password; private Boolean enabled; private Boolean locked; private List<Role> roles; @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return !locked; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return enabled; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getLocked() { return locked; } public void setLocked(Boolean locked) { this.locked = locked; } public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } }
程式碼解釋:
使用者實體類需要實現 UserDetails 介面,並且實現該介面中的 7 個方法
| 方法名 | 解釋 |
| — | — |
| getAuthorities() | 獲取當前使用者物件所具有的角色資訊 |
| getPassword() | 獲取當前使用者物件的密碼 |
| getUsername() | 獲取當前使用者物件的使用者名稱 |
| isAccountNonExpired() | 當前賬號是否未過期 |
| isAccountNonLocked() | 當前賬號是否未鎖定 |
| isCredentialsNonExpired() | 當前賬號密碼是否未過期 |
| isEnabled() | 當前賬號是否可用 |
使用者根據實際情況設定這 7 個方法的返回值。因為預設情況下不需要開發者自己進行密碼角色等資訊的比對,開發者只需要提供相關資訊即可,例如 getPassword() 方法返回的密碼和使用者輸入的密碼不匹配,會自動丟擲 BadCredentialsException 異常,isAccountNonExpired() 方法返回了 false ,會自動丟擲 AccountExpiredException 異常,因此對開發者而言,只需要按照資料庫中的資料在這裡返回相應的設定即可。此處因為資料庫中只有 enabled 和 locked 欄位,故帳號未過期和密碼未過期兩個方法都返回 true
getAuthorities() 用來獲取當前使用者所具有的角色資訊,此處使用者所具有的角色儲存在 roles 屬性中,因此該方法直接遍歷 roles 屬性,然後構造 SimpleGrantedAuthority 集合並返回
@Service public class UserService implements UserDetailsService { @Autowired UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userMapper.loadUserByUsername(username); if (user == null) { throw new UsernameNotFoundException("賬戶不存在!"); } user.setRoles(userMapper.getUserRolesByUid(user.getId())); return user; } }
程式碼解釋:
涉及到的 UserMapper 和 UserMapper.xml 如下
@Mapper public interface UserMapper { User loadUserByUsername(String username); List<Role> getUserRolesByUid(Integer 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="org.sang.mapper.UserMapper"> <select id="loadUserByUsername" resultType="org.sang.model.User"> select * from user where username=#{username} </select> <select id="getUserRolesByUid" resultType="org.sang.model.Role"> select * from role r,user_role ur where r.id=ur.rid and ur.uid=#{id} </select> </mapper>
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserService userService; @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .antMatchers("/db/**").hasRole("dba") .antMatchers("/user/**").hasRole("user") .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/login").permitAll() .and() .csrf().disable(); } }
此處的設定與上篇介紹的一致,唯一不同的是沒有設定記憶體使用者,而是將剛剛建立好的 UserService 設定到 AuthenticationManagerBuilder 中。
@RestController public class HelloController { @GetMapping("/admin/hello") public String admin() { return "hello admin"; } @GetMapping("/db/hello") public String dba() { return "hello dba"; } @GetMapping("/user/hello") public String user() { return "hello user"; } }
登入 admin 使用者,存取 /admin/hello,報了以下錯誤
Invalid bound statement (not found)
pom.xml 新增以下設定
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build>
再次登入存取 /admin/hello,報了以下錯誤
Caused by: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Illegal overloaded getter method with ambiguous type for property enabled in class class org.sang.model.User. This breaks the JavaBeans specification and can cause unpredictable results.
去掉 User 實體類中 enabled 屬性的 get set 方法,如下
public class User implements UserDetails { private Integer id; private String username; private String password; private Boolean enabled; private Boolean locked; private List<Role> roles; @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return !locked; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return enabled; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public Boolean getLocked() { return locked; } public void setLocked(Boolean locked) { this.locked = locked; } public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } }
再次登入存取 /admin/hello
存取 /db/hello
存取 /user/hello
到此這篇關於SpringBoot淺析安全管理之基於資料庫認證的文章就介紹到這了,更多相關SpringBoot資料庫認證內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45