Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01
這個(gè)小項(xiàng)目包含了注冊(cè)與登錄,使用了springboot+mybatis+shiro的技術(shù)棧;當(dāng)用戶在瀏覽器登錄時(shí)發(fā)起請(qǐng)求時(shí),首先這一系列的請(qǐng)求會(huì)被攔截器進(jìn)行攔截(ShiroFilter),然后攔截器根據(jù)用戶名去數(shù)據(jù)庫尋找是否有相對(duì)應(yīng)的user實(shí)體;如果有則返回封裝到User類中(沒有就用戶名錯(cuò)誤),然后比對(duì)密碼是否一致;如果都通過了則認(rèn)證成功;登錄到主頁面;然后主頁面有不同的功能,不同的用戶擁有不同的權(quán)限,有的能看到,有的則無法看到;然后如果不登陸直接訪問主頁面,會(huì)被強(qiáng)制跳轉(zhuǎn)到登錄頁面;另外登錄之后也可以退出登錄;
通過分析上文:https://blog.csdn.net/Kevinnsm/article/details/11187650
1.首先快速創(chuàng)建一個(gè)springboot項(xiàng)目(使用spring向?qū)?#xff09;
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.1</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.hao</groupId><artifactId>springboot-shiro</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-shiro</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><!--########################################################--><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.7.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.19</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency></dependencies><!--########################################################--><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>2.數(shù)據(jù)庫建表
這里的salt字段主要是為了使用md5+salt+散列加密時(shí),需要保存salt字符串,以便在業(yè)務(wù)層比較密碼正確與否時(shí)一致
3.注冊(cè)、登錄和主頁面
<%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %> <!doctype html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title> </head> <body><h1>用戶注冊(cè)</h1><form action="${pageContext.request.contextPath}/user/register" method="post">username:<input type="text" name="username"><br>password:<input type="password" name="password"><br><input type="submit" value="注冊(cè)"></form> </body> </html> <%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %> <!doctype html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title> </head> <body><h1>用戶登錄</h1><form action="${pageContext.request.contextPath}/user/login" method="post">username:<input type="text" name="username"><br>password:<input type="password" name="password"><br><input type="submit" value="登錄"></form> </body> </html> <%@page contentType="text/html; utf-8" pageEncoding="UTF-8" isELIgnored="false" %> <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <!doctype html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title> </head> <body><h1>系統(tǒng)主頁,歡迎你的到來</h1><a href="${pageContext.request.contextPath}/user/outLogin">退出</a><ul><shiro:hasAnyRoles name="user"><li><a href="">用戶管理</a><ul><shiro:hasPermission name="user:add:*"><li><a href="">添加用戶</a></li></shiro:hasPermission><li><a href="">刪除用戶</a></li><li><a href="">修改用戶</a></li><li><a href="">查詢用戶</a></li></ul></li></shiro:hasAnyRoles><shiro:hasRole name="admin"><li><a href="">商品管理</a> </li><li><a href="">訂單管理</a> </li><li><a href="">物流管理</a> </li></shiro:hasRole><shiro:hasRole name="shper"><li><a href="">終極格式化</a> </li></shiro:hasRole></ul> </body> </html>4.配置文件
spring.application.name=shiro server.servlet.context-path=/shirospring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=hao20001010mybatis.type-aliases-package=com.hao.springboot.entity mybatis.mapper-locations=classpath:mapper/*.xmllogging.level.com.hao.springboot.dao=debug5.編寫mapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hao.springboot.dao.UserDao"><insert id="save" parameterType="User">insert into t_user values(#{id},#{username},#{password},#{salt})</insert><select id="findByUserName" resultType="User">select * from t_user where username=#{username}</select> </mapper>6.DAO層
@Repository public interface UserDao {void save(User user);User findByUserName(String username); }7.service層
public interface UserService {void register(User user);User findByUserName(String username); } @Service("userService") @Transactional public class UserServiceImpl implements UserService {@AutowiredUserDao userDao;@Overridepublic void register(User user) {//明文密碼進(jìn)行md5+salt+隨機(jī)散列//1.生成隨機(jī)鹽String salt = SaltUtil.getSalt(8);//2.將隨機(jī)鹽保存到數(shù)據(jù)庫中user.setSalt(salt);Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);user.setPassword(md5Hash.toHex());userDao.save(user);}@Overridepublic User findByUserName(String username) {return userDao.findByUserName(username);} }8.controller層
/*** @author:抱著魚睡覺的喵喵* @date:2020/12/29* @description:*/ @Controller @RequestMapping("/user") public class UserController {@AutowiredUserService userService;/*** 處理身份認(rèn)證* @param username* @param password* @return*/@RequestMapping("/login")public String login(@RequestParam("username")String username,@RequestParam("password")String password){//獲取主體對(duì)象Subject subject = SecurityUtils.getSubject();try {subject.login(new UsernamePasswordToken(username,password));return "redirect:/index.jsp";}catch (UnknownAccountException e){e.printStackTrace();System.out.println("用戶名錯(cuò)誤");}catch (IncorrectCredentialsException e){e.printStackTrace();System.out.println("密碼錯(cuò)誤");}return "redirect:/login.jsp";}/*** 退出用戶* @return*/@RequestMapping("/outLogin")public String outLogin(){Subject subject = SecurityUtils.getSubject();subject.logout(); //退出用戶return "redirect:/login.jsp";}/*** 用戶注冊(cè)* @return*/@RequestMapping("/register")public String register(User user){try {userService.register(user);return "redirect:/login.jsp";}catch (Exception e){e.printStackTrace();return "redirect:/register.jsp";}} }9.User實(shí)體類
@Data @ToString @AllArgsConstructor @Accessors(chain = true) @NoArgsConstructor public class User {private String id;private String username;private String password;private String salt;}10.創(chuàng)建ShiroFilter攔截器(攔截所有請(qǐng)求)
import com.hao.springboot.shiro.realm.CustomerRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import java.util.HashMap; import java.util.Map;/*** @author:抱著魚睡覺的喵喵* @date:2020/12/29* @description:*/ @Configuration public class ShiroConfig {/*** 1.創(chuàng)建ShiroFilter* 負(fù)責(zé)攔截所有請(qǐng)求* @return shiroFilterFactoryBean*/@Beanpublic ShiroFilterFactoryBean getShiroFilterFactory(DefaultWebSecurityManager defaultWebSecurityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//給filter設(shè)置安全管理器shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);//配置系統(tǒng)首先資源//配置系統(tǒng)公共資源Map<String,String> map=new HashMap<>();map.put("/user/login","anon"); //設(shè)置為公共資源,防止登錄死循環(huán)map.put("/user/register","anon");map.put("/register.jsp","anon");map.put("/**","authc"); //authc 這個(gè)資源需要認(rèn)證和授權(quán)shiroFilterFactoryBean.setFilterChainDefinitionMap(map);//默認(rèn)界面路徑shiroFilterFactoryBean.setLoginUrl("/login.jsp");return shiroFilterFactoryBean;}/*** 安全管理器* @return defaultWebSecurityManager*/@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();defaultWebSecurityManager.setRealm(realm);return defaultWebSecurityManager;}/**** @return*/@Bean("realm")public Realm getRealm(){CustomerRealm customerRealm = new CustomerRealm();//修改憑證校驗(yàn)匹配器HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();//設(shè)置加密算法為md5credentialsMatcher.setHashAlgorithmName("MD5");//設(shè)置散列次數(shù)credentialsMatcher.setHashIterations(1024);customerRealm.setCredentialsMatcher(credentialsMatcher);return customerRealm;} }11.自定義realm
package com.hao.springboot.shiro.realm;import com.hao.springboot.entity.User; import com.hao.springboot.service.UserService; import com.hao.springboot.utils.ApplicationContextUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.util.ObjectUtils;import java.util.Arrays;/*** @author:抱著魚睡覺的喵喵* @date:2020/12/29* @description: 自定義realm完成用戶認(rèn)證和授權(quán)*/ public class CustomerRealm extends AuthorizingRealm {/*** 用戶授權(quán)* @param principalCollection* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("調(diào)用權(quán)限認(rèn)證:"+principalCollection);String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();if ("Ronin".equals(primaryPrincipal)){simpleAuthorizationInfo.addRoles(Arrays.asList("user","admin","shaper"));simpleAuthorizationInfo.addStringPermission("user:add:*");return simpleAuthorizationInfo;} // else if("tom".equals(primaryPrincipal)){ // simpleAuthorizationInfo.addRole("admin"); // return simpleAuthorizationInfo; // }return null;}/*** 用戶認(rèn)證* @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {String principal = (String) authenticationToken.getPrincipal();//在工廠中獲取業(yè)務(wù)對(duì)象UserService userService = (UserService) ApplicationContextUtils.getBean("userService");User user = userService.findByUserName(principal);if (!ObjectUtils.isEmpty(user)){return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());}return null;} }12.編寫salt(因?yàn)橐褂胢d5+salt+散列對(duì)密碼加密)
package com.hao.springboot.utils;import java.util.Random;/*** @author:抱著魚睡覺的喵喵* @date:2020/12/29* @description:*/ public class SaltUtil {/*** 生成salt的靜態(tài)方法* @param n* @return*/public static String getSalt(int n){char[] chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()".toCharArray();StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < n; i++) {char aChar = chars[new Random().nextInt(chars.length)];stringBuilder.append(aChar);}return stringBuilder.toString();} //測(cè)試一下public static void main(String[] args) {System.out.println(getSalt(8));} }13.我們想要在自定義realm中的doGetAuthenticationInfo()方法調(diào)用service層,所以需要
使用靜態(tài)類獲取
14,測(cè)試
訪問aaaa
強(qiáng)制到登錄頁面
以下是事先的數(shù)據(jù)
輸入Ronin && 123登錄
可以看到Ronin用戶能夠看所有的權(quán)限信息
接著點(diǎn)擊退出
輸入tom && 123
點(diǎn)擊登錄
可以發(fā)現(xiàn)tom用戶什么都看不到,因?yàn)闆]有賦給tom用戶任何權(quán)限
使用代碼控制角色權(quán)限
@Controller @RequestMapping("/limit") public class LimitController {@RequestMapping("/save")@RequiresRoles(value = {"admin","user"})@RequiresPermissions("user:update:*")public String save(){ // Subject subject = SecurityUtils.getSubject(); // if (subject.hasRole("admin")) { // System.out.println("保存訂單!"); // }else { // System.out.println("無權(quán)訪問"); // }System.out.println("進(jìn)入方法");return "redirect:/index.jsp";} }我從這個(gè)小案例中我學(xué)到了什么
1.使用spring框架,所有的bean都是通過容器管理的;想要使用某個(gè)組件直接從容器中拿就行了,比如service注入到controller即可;本案例中的sevice層需要被自定義的realm調(diào)用,進(jìn)行密碼認(rèn)證,但是怎么調(diào)用就成了問題,不可能new一個(gè)吧,所有bean對(duì)象都是通過IOC容器管理的;
所以只有使用靜態(tài)類,將該靜態(tài)類加入容器中(使用@Component注解)
2.復(fù)習(xí)了ApplicationContext和BeanFactory的區(qū)別
3.shiro與springboot的整合關(guān)鍵在于ShiroFilter攔截器的使用
2021一起加油;
總結(jié)
以上是生活随笔為你收集整理的Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shiro+springboot分析思路
- 下一篇: Shiro+springboot+myb