SpringBoot 2.0 | Security+Mybatis 許可權認證
阿新 • • 發佈:2018-11-29
1.簡介
Spring Security 是 Spring 家族的一個安全框架, 提供了全面的安全解決方案 , 對使用者的身份進行認證, 以及驗證每一個使用者所具有的的許可權, 根據使用者的許可權限制使用者的操作。
Mybatis 是一款優秀的持久層框架 , 支援自定義 SQL 以及各種高階對映 , 與 JPA 的自動生成 SQL 相比, 它更加靈活, 本例使用 Mybatis 儲存使用者的身份和許可權, 通過 Security 獲取使用者資訊, 對使用者的許可權和操作進行管理。
2.實現程式碼
1.專案配置
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver username: root password: roof url: jdbc:mysql://localhost:3306/security?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true # 配置前端Thymeleaf模板引擎 thymeleaf: # 打包末尾無/ prefix: classpath:/templates/ check-template-location: true suffix: .html encoding: UTF-8 servlet: content-type: text/html mode: HTML5 # 禁止後實現前端熱部署 cache: false # 整合Mybatis mybatis: # Mybatis對映 mapper-locations: classpath:mapper/*.xml type-aliases-package: com.hly.springBootSecurityMybatis.entity # 埠設定 server: port: 8081
2.Security 配置
@Configuration @EnableWebSecurity//開啟WebSecurity功能 @EnableGlobalMethodSecurity(prePostEnabled = true)//開啟方法上的保護 public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean UserDetailsService userService(){ return new UserService(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { //從資料庫中獲取使用者認證資訊 auth.userDetailsService(userService()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //不需要驗證的資源 .antMatchers("/css/**", "/index").permitAll() //需要驗證,角色為Role .antMatchers("/article/**").hasAnyRole("ADMIN","STUDENT","TEACHER") .antMatchers("/admin/**").hasAnyRole("ADMIN","STUDENT","TEACHER") .and() //表單的登入地址和失敗地址 .formLogin().loginPage("/login").failureForwardUrl("/loginError") .and() //異常處理介面 .exceptionHandling().accessDeniedPage("/401"); http.logout().logoutSuccessUrl("/"); } }
3.controller 層
@Controller public class ArticleController { @Autowired ArticleService articleService; /** * 檢視文章列表 * @param model * @return */ @RequestMapping("/article") public ModelAndView articleList(Model model){ List<Article> list = articleService.getArticles(); model.addAttribute("articlesList",list); return new ModelAndView("article/list","articleModel",model); } /** * 給方法設定許可權,沒有ADMIN許可權的使用者不能刪除文章 * @param id * @param model * @return */ @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") @GetMapping(value = "/article/{id}/deletion") public ModelAndView delete(@PathVariable("id")int id,Model model){ articleService.deleteArticle(id); model.addAttribute("articlesList",articleService.getArticles()); return new ModelAndView("article/list","articleModel",model); } }
@Controller
public class LoginController {
@RequestMapping("/")
public String root(){
return "redirect:/index";
}
@RequestMapping("index")
public String index(){
return "index";
}
//@RequestMapping將接收Get,Post,Head,Options等所有的請求方式
@RequestMapping(value = "/login")
public String login(){
return "login";
}
@RequestMapping("/loginError")
public String loginError(ModelAndView modelAndView){
modelAndView.addObject("loginError",true);
return "login";
}
@RequestMapping("/admin")
public String admin(){
return "admin/admin";
}
//@RequestMapping(method = RequestMethod.GET)的縮寫
@GetMapping("401")
public String error(){
return "401";
}
@GetMapping("/logout")
public String logout(){
return "/";
}
}
4.dao 層
@Repository
public interface UserDao {
//通過使用者名稱查詢使用者
public User findUserByUsername(String username);
}
5. entity
Article
public class Article {
private int id;
private String title;
private String content;
public Article() {
}
public Article(int id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Article{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
Role
public class Role implements GrantedAuthority {
private int id;
private String name;
@Override
public String getAuthority() {
return name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
User
public class User implements UserDetails, Serializable {
private int id;
private String username;
private String password;
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
6.service層
@Service
public class ArticleServiceImpl implements ArticleService {
private List<Article> list = new ArrayList<>();
public ArticleServiceImpl() {
list.add(new Article(1,"java","java從入門到搬磚"));
list.add(new Article(2,"SQL","SQL從刪庫到跑路"));
}
@Override
public List<Article> getArticles() {
return list;
}
@Override
public void deleteArticle(int id) {
Iterator iter = list.iterator();
while(iter.hasNext()){
Article article = (Article)iter.next();
if(article.getId()==id){
iter.remove();
}
}
}
}
public interface ArticleService {
List<Article> getArticles();
void deleteArticle(int id);
}
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.err.println(userDao.findUserByUsername(username));
return userDao.findUserByUsername(username);
}
}
7.mapper
<?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="com.hly.springBootSecurityMybatis.dao.UserDao">
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<collection property="roles" ofType="Role">
<result property="name" column="name"/>
</collection>
</resultMap>
<select id="findUserByUsername" parameterType="string" resultMap="userMap">
SELECT u.*,r.name FROM user u
LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON ur.role_id = r.id
WHERE username = #{username}
</select>
</mapper>
8.前端頁面
admin.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>擁有許可權才能訪問該頁面</h3>
<p><a href="../../templates/article/list.html" th:href="@{/article}" >管理文章</a></p>
<p><a href="../../templates/index.html" th:href="@{/index}" >返回首頁</a></p>
<div th:substituteby="index::user"></div>
</body>
</html>
list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>list</title>
</head>
<body>
<h3>文章管理,ADMIN角色才能刪除</h3>
<table>
<tr>
<td><b>文章編號</b></td>
<td><b>文章標題</b></td>
<td><b>文章內容</b></td>
</tr>
<tr th:each="article:${articlesList}">
<td th:text="${article.id}"></td>
<td th:text="${article.title}"></td>
<td th:text="${article.content}"></td>
<td><a href="" th:href="@{'/article/'+${article.id}+'/deletion'}">刪除</a></td>
</tr>
</table>
<div th:substituteby="index::user"></div>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>首頁任何角色都能訪問</h3>
<p><a href="../templates/admin/admin.html" th:href="@{/admin}">檢視被保護介面: /admin</a></p>
<div th:fragment="user" sec:authorize="isAuthenticated()">
登入使用者:<span sec:authentication="name"></span>
使用者角色:<span sec:authentication="principal.authorities"></span>
<div>
<form th:action="@{/logout}" method="post">
<input type="submit" value="退出">
</form>
</div>
</div>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>登入頁面</h2>
<p th:if="${loginError}">使用者名稱或密碼錯誤</p>
<form th:action="@{/login}" method="post">
<label for="username">使用者名稱</label>
<input type="text" id="username" name="username" autofocus="autofocus" autocomplete="new-text">
<label for="password">密碼</label>
<input type="password" id="password" name="password" autocomplete="new-password">
<input type="submit" value="login">
<p><a href="/index" th:href="@{/index}">返回首頁</a></p>
</form>
</body>
</html>
9.資料庫如下
/*
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `role`
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'ROLE_ADMIN');
INSERT INTO `role` VALUES ('2', 'ROLE_TEACHER');
INSERT INTO `role` VALUES ('3', 'ROLE_STUDENT');
INSERT INTO `role` VALUES ('4', 'ROLE_COUNSELOR');
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_USERNAME` (`username`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'admin', '$2a$10$NmtmORbN/ATToou17gvjl.CUu1yTNxxRjsO2GOJUbJFsWd21pYmFi');
INSERT INTO `user` VALUES ('2', 'js', '$2a$10$NmtmORbN/ATToou17gvjl.CUu1yTNxxRjsO2GOJUbJFsWd21pYmFi');
INSERT INTO `user` VALUES ('3', 'xs', '$2a$10$NmtmORbN/ATToou17gvjl.CUu1yTNxxRjsO2GOJUbJFsWd21pYmFi');
INSERT INTO `user` VALUES ('4', 'fdy', '$2a$10$NmtmORbN/ATToou17gvjl.CUu1yTNxxRjsO2GOJUbJFsWd21pYmFi');
-- ----------------------------
-- Table structure for `user_role`
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
KEY `FKuser_id` (`role_id`),
KEY `FKrole_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1');
INSERT INTO `user_role` VALUES ('2', '2');
INSERT INTO `user_role` VALUES ('3', '3');
INSERT INTO `user_role` VALUES ('4', '4');
10.整個專案結構
3.測試結果
我們在 security 裡配置了 Index 頁面不需要驗證即可訪問,訪問
http://localhost:8081/index
在沒有登入之前,通過瀏覽器訪問其他任何頁面,都會跳轉到登入介面。
登入之後即可進入管理頁面,這裡的加密密碼都是123,賬號請看資料庫。
在管理文章裡面,我們在 controller 層配置了只有 ADMIN 使用者才能刪除,其他使用者如果點選刪除將會提示沒有許可權。
原始碼:https://github.com/huangliangyun/Spring-Boot-2.X
QQ交流群:865061230
參考<<深入理解SpringCloud微服務構建>>