自己做的網站外國人能訪問嗎口碑營銷方案
文章目錄
- 前言
- 一、springBoot+shiro環(huán)境準備
- 1.數(shù)據(jù)庫
- 2.ssmp環(huán)境搭建
- 3.實體類
- 4.三層搭建
- 5.初始化測試數(shù)據(jù)
- 二、Shiro過濾器
- 1.Shiro認證過濾器
- 2.Shiro授權過濾器
- 三、springBoot+shiro身份認證
- 1.創(chuàng)建Realm,重寫認證方法doGetAuthenticationInfo
- 2.創(chuàng)建shiro配置類
- 3.Postman測試
- 四、springBoot+shiro授權,鑒權
- 1.重寫授權方法doGetAuthenticationInfo
- 2.訪問程序資源(鑒權)
- 3.全局異常處理器
- 4.Postman測試
前言
整合springBoot+shiro流程:
- 環(huán)境準備
- 身份認證
2.1 密碼加密
2.2 非法請求控制 - 授權,鑒權
一、springBoot+shiro環(huán)境準備
項目目錄:
1.數(shù)據(jù)庫
認證框架五表設計:
準備user用戶表、user_role用戶角色關系表、role角色表、 role_permission角色權限關系表、permission權限表;
user用戶表:
user_role用戶角色關系表:
role角色表:
role_permission角色權限關系表:
permission權限表:
2.ssmp環(huán)境搭建
2.1準備依賴
<!--web啟動器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--test啟動器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--Mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version><scope>runtime</scope></dependency><!--Mybatisplus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><!--lombok作用于實體類--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--shiro相關坐標--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version></dependency>
2.2 配置Yaml文件
#數(shù)據(jù)源配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rooturl: jdbc:mysql://localhost:3306/shiro?serverTimezone=GMTpassword: 12345678
#Mybatisplus配置
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.實體類
User用戶類:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_user")
public class User implements Serializable {@TableId(value = "id",type = IdType.NONE)private String id;@TableField(value = "username")private String username;@TableField(value = "password")private String password;@TableField(value = "salt")private String salt;@TableField(exist = false)private Set<Role> roles;
}
role角色類:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_role")
public class Role implements Serializable {private String id;private String name;private String code;private String description;private Set<Permission> permissions;
}
Permission 權限類:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("pe_permission")
public class Permission implements Serializable {private String id;private String name;private String code;private String escription;
}
4.三層搭建
DAO層: 因為是五表構造,需要處理關聯(lián)關系,所以在這里使用級聯(lián)查詢的方式實現(xiàn):
UserMapper :
@Mapper
public interface UserMapper extends BaseMapper<User> {@Select("select * from pe_user where username =#{username}")User findByName(String name);@Select("select * from pe_user where id =#{id}")@Results({@Result(id = true,property = "id",column = "id"),@Result(property = "username",column = "username"),@Result(property = "password",column = "password"),@Result(property = "salt",column = "salt"),@Result(property = "roles",column = "id",many = @Many(select = "com.apesource.springboot_shiro_01.dao.RoleMapper.findRoleById"))})User findUserDetailById(int id);
}
RoleMapper :
@Mapper
public interface RoleMapper {@Results({@Result(column = "id", property = "id"),@Result(column = "name", property = "name"),@Result(column = "code", property = "code"),@Result(column = "description", property = "description"),@Result(column = "id",property = "permissions",many = @Many(select = "com.apesource.springboot_shiro_01.dao.PermissionMapper.findPermissionById"))})
// @Select("select * from 角色表 where 角色id in (select 角色ID from 關系表 where 用戶id = ?)")@Select("select * from pe_role where id in (select role_id from pe_user_role where user_id = #{id})")public Set<Role> findRoleById(String id);
}
PermissionMapper :
@Mapper
public interface PermissionMapper {@Select("select * from pe_permission where id in (select permission_id from pe_role_permission where role_id = #{id})")@Results({@Result(column = "id", property = "id"),@Result(column = "name", property = "name"),@Result(column = "code",property = "code"),@Result(column = "description", property = "description")})public Set<Permission> findPermissionById(long id);
}
Service層: 因為項目只實現(xiàn)Shrio的用戶認證和授權,所以只構造User的業(yè)務層就足夠了。
UserService :
@Service
public class UserService {@Autowired(required = false)UserMapper mapper;public User findByName(String name){return mapper.findByName(name);}public User findUserDetailById(int id){return mapper.findUserDetailById(id);}
}
Controller層:
@RestController
public class ShiroController {@AutowiredUserService service;/*** @RequiresPermissions() -- 訪問此方法必須具備的權限* @RequiresRoles() -- 訪問此方法必須具備的角色**/@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "訪問個人主頁成功";}//添加@RequiresPermissions("user-add")@RequestMapping(value = "/user",method = RequestMethod.POST)public String add() {return "添加用戶成功";}//查詢@RequiresPermissions("user-find")@RequestMapping(value = "/user/find",method = RequestMethod.GET )public String find(){return "查詢成功";}//更新@RequiresPermissions("user-update")@RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)public String update(@PathVariable String id) {return "更新用戶成功";}//刪除@RequiresPermissions("user-delete")@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)public String delete(@PathVariable String id) {return "刪除用戶成功";}//未登陸與未授權頁面@RequestMapping(value="/autherror")public String autherror() {return "未登錄";}//用戶登錄@RequestMapping(value="/login")public String login(User user) {try {//1.構造登錄令牌UsernamePasswordToken upToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());//2.獲取subjectSubject subject = SecurityUtils.getSubject();//3.調用subject進行登錄subject.login(upToken);return "登錄成功";}catch (Exception e) {e.printStackTrace();return "用戶名或密碼錯誤";}}
}
5.初始化測試數(shù)據(jù)
因為在實戰(zhàn)場景下,數(shù)據(jù)庫的密碼是不可以明文保存的,這樣對于數(shù)據(jù)的安全性是巨大的隱患,而shiro也支持數(shù)據(jù)加密的功能。
加密工具類:
public class DigestUtil {//算法方式public static final String SHA1 = "SHA-1";public static final String SHA256 = "SHA-256";//加密次數(shù)public static final Integer Counts =369;/*** @Description show* @param input 需要散列字符串* @param salt 鹽字符串* @return*/public static String show(String input,String salt){return new SimpleHash(SHA1,input,salt,Counts).toString();}/*** @Description 隨機獲得salt字符串* @return*/public static String generateSalt(){SecureRandomNumberGenerator randomNumberGenerator =new SecureRandomNumberGenerator();return randomNumberGenerator.nextBytes().toHex();}/*** @Description 生成密碼字符密文和salt密文* @param* @return*/public static Map<String,String> entryptPassword(String passwordPlain){Map<String,String> map = new HashMap<>();String salt =generateSalt();String password = show(passwordPlain,salt);map.put("salt",salt);map.put("明文password",passwordPlain);map.put("密文密碼",password);return map;}
由于沒有實現(xiàn)注冊功能,所以對于數(shù)據(jù)庫的密文密碼通過自己操作工具類輸入進去:
public static void main(String[] args) {String name ="張三豐";String password = "123123";Map<String, String> map = entryptPassword(password);System.out.println(map.toString());}
二、Shiro過濾器
Shiro內置了很多默認的過濾器,比如身份驗證、授權等相關的,shiro也是通過過濾器的原理來實現(xiàn)認證和授權的擴展功能的。
1.Shiro認證過濾器
2.Shiro授權過濾器
三、springBoot+shiro身份認證
1.創(chuàng)建Realm,重寫認證方法doGetAuthenticationInfo
使用@PostConstruct注解修飾的init方法就會在Spring容器的啟動時自動的執(zhí)行
public class MyRealm extends AuthorizingRealm {@AutowiredUserService service;/*** @Description 自定義密碼比較器* bean標簽 init-method屬性*/@PostConstructpublic void initCredentialsMatcher() {//指定密碼算法HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestUtil.SHA1);//指定迭代次數(shù)hashedCredentialsMatcher.setHashIterations(DigestUtil.Counts);//生效密碼比較器setCredentialsMatcher(hashedCredentialsMatcher);}
}//認證@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.獲取登錄的用戶名密碼(token)UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;String username = token.getUsername();//2.根據(jù)用戶名查詢數(shù)據(jù)庫User user = service.findByName(username);//3.判斷用戶是否存在或者密碼是否一致if(user==null){throw new UnknownAccountException("賬戶不存在");}//4.如果一致返回安全數(shù)據(jù)//通過SimpleAuthenticationInfo校驗數(shù)據(jù)//構造方法:安全數(shù)據(jù),密碼(匿名),混淆字符串(salt),realm域名return new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"MyRealm");}
2.創(chuàng)建shiro配置類
shiro配置類需要配置八個步驟,比較繁瑣,最重要的是shiroFilter過濾器,用于實現(xiàn)非法請求的處理。
@Configuration
public class ShiroConfiguration {/*** 1.創(chuàng)建shiro自帶cookie對象*/@Beanpublic SimpleCookie sessionIdCookie(){SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("ShiroSession");return simpleCookie;}//2.創(chuàng)建realm@Beanpublic MyRealm getRealm() {return new MyRealm();}/*** 3.創(chuàng)建會話管理器*/@Beanpublic DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(false);sessionManager.setSessionIdCookieEnabled(true);sessionManager.setSessionIdCookie(sessionIdCookie());sessionManager.setGlobalSessionTimeout(3600000);return sessionManager;}//4.創(chuàng)建安全管理器@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(getRealm());securityManager.setSessionManager(sessionManager());return securityManager;}/*** 5.保證實現(xiàn)了Shiro內部lifecycle函數(shù)的bean執(zhí)行*/@Bean(name = "lifecycleBeanPostProcessor")public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 6.開啟對shiro注解的支持* AOP式方法級權限檢查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 7.配合DefaultAdvisorAutoProxyCreator事項注解權限校驗*/@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());return authorizationAttributeSourceAdvisor;}//8.配置shiro的過濾器工廠再web程序中,shiro進行權限控制全部是通過一組過濾器集合進行控制@Beanpublic ShiroFilterFactoryBean shiroFilter() {//1.創(chuàng)建過濾器工廠ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();//2.設置安全管理器filterFactory.setSecurityManager(defaultWebSecurityManager());//3.通用配置(跳轉登錄頁面,為授權跳轉的頁面)filterFactory.setLoginUrl("/autherror");//跳轉url地址//4.設置過濾器集合//key = 攔截的url地址//value = 過濾器類型Map<String,String> filterMap = new LinkedHashMap<>();//key:請求規(guī)則 value:過濾器名稱filterMap.put("/login","anon");//當前請求地址可以匿名訪問filterMap.put("/user/**","authc");//當前請求地址必須認證之后可以訪問//在過濾器工程內設置系統(tǒng)過濾器filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}
}
3.Postman測試
成功:
合法請求資源:
失敗:
非法請求資源:
四、springBoot+shiro授權,鑒權
1.重寫授權方法doGetAuthenticationInfo
授權方法:
操作的時候,判斷用戶是否具有響應的權限
一定先認證再授權
先認證 – 安全數(shù)據(jù)
再授權 – 根據(jù)安全數(shù)據(jù)獲取用戶具有的所有操作權限
//授權@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//1.獲取已認證的用戶數(shù)據(jù)String id1 = (String) principalCollection.getPrimaryPrincipal();int i = Integer.parseInt(id1);User user = service.findUserDetailById(i);//2.根據(jù)用戶數(shù)據(jù)獲取用戶的權限信息(所有角色,所有權限)Set<String> roles = new HashSet<>();//所有角色Set<String> perms = new HashSet<>();//所有權限for (Role role : user.getRoles()) {roles.add(role.getName());for (Permission perm : role.getPermissions()) {perms.add(perm.getCode());}}//將角色和權限信息返回SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(perms);info.setRoles(roles);return info;}
2.訪問程序資源(鑒權)
注解實現(xiàn):直接在控制器接口上加注解即可;
注解實現(xiàn)權限:如果權限信息不匹配,拋出異常(則使用異常處理器解決);
常用注解:
@RequiresPermissions – 訪問此方法必須具備的權限
@RequiresRoles – 訪問此方法必須具備的角色
@RequiresAuthentication – 表明當前用戶需是經過認證的用戶
@ RequiresGuest --表明該用戶需為”guest”用戶
@ RequiresUser – 當前用戶需為已認證用戶或已記住用戶
具體實現(xiàn)在controller層的每個業(yè)務接口,這里只舉例一個:
//個人主頁@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "訪問個人主頁成功";}@RequiresPermissions("user-add")//參數(shù):對應權限的code值@RequestMapping(value = "/user",method = RequestMethod.POST)public String add() {return "添加用戶成功";}
3.全局異常處理器
專門用于處理權限不足的異常信息:
@ControllerAdvice
public class BaseExceptionHandler {@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e) {return "未授權-異常處理器實現(xiàn)";}
}
4.Postman測試
張三豐用戶只有user-home權限,沒有add權限;
1.直接訪問權限方法會被攔截做登錄:
2.訪問有權限的資源
成功
3.訪問沒有權限的資源
失敗,被全局異常處理器處理。