代码包括基于注解权限和加密加盐
项目结构
1.新建maven工程,并导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot集成Junit 的启动依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--thymeleaf启动依赖,它是一个前端模板引擎,完全替代jsp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<!--引入的是shiro与spring的集成-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--aop 的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--thymeleaf shiro 根据权限标签显示-->
<!--shiro与themeleaf集成-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
2.在resource文件夹下新建application.properties
#日志输出级别
logging.level.root=debug
3.在com.aaa下新建启动类
package com.aaa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class,args);
}
}
4.新建自定义realm
package com.aaa.realm;
import org.apache.shiro.authc.*;
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;
public class UserRealm extends AuthorizingRealm {
// 获取认证信息
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
System.out.println("username=="+username);
// 需要自己取数据库校验当前用户名是否存在 如果不存在,用改手动方式抛出
if (!username.equals("root")){
throw new UnknownAccountException();
}
// 根据据用户名 去数据库查询密码
// 假装去数据获取对应用户名的密码
String passwordFromDB = "123456";
//数据库真实的密码
//String passwordFromDB="db5f14348f3016b9d53c66db0ed6bb8e";
//加盐
ByteSource salt=ByteSource.Util.bytes(username);
// 得到用户 真实的数据哭的用户名 密码认证信息
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,passwordFromDB,this.getName());
//返回给securityMange 进行认证
return authenticationInfo;
}
// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
System.out.println("username:"+username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 去数据库查询 root 相关的权限 和 角色
if (username.endsWith("root")){
// 设置当前用户角色
authorizationInfo.addRole("boss");
// 给当前用户设置权限
authorizationInfo.addStringPermission("goods:add");
authorizationInfo.addStringPermission("goods:delete");
authorizationInfo.addStringPermission("user:delete");
}
// 返回个securityManager 当前用户的角色 和权限
return authorizationInfo;
}
}
5.在com.aaa 新建shiro 的配置类,新建Config文件夹,创建Shiroconfig类
package com.aaa.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.aaa.realm.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@Configuration// 表明当前类是一个配置类 相当于beans.xml 文件
public class Shiroconfig {
@Bean // 1.将创建的UserRealm 加入到容器
public UserRealm getUserRealm(HashedCredentialsMatcher matcher){
UserRealm userRealm=new UserRealm();
//将userRealm和matcher绑定
userRealm.setCredentialsMatcher(matcher);
return userRealm;
}
/*@Bean//将用户传来的密码加密,并获取SimpleAuthenticationInfo中的盐和迭代1000次,就是当前用户的登录的加密密码
public HashedCredentialsMatcher getHashedCredentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
//算法名
credentialsMatcher.setHashAlgorithmName("MD5");
//加密迭代次数
credentialsMatcher.setHashIterations(1000);
return credentialsMatcher;
}*/
@Bean// 2.向容器加入 SecurityManager getSecurityManager(UserRealm userRealm)
// 当创建SecurityManager时,回去容器中去查找一个UserRealm类型的bean
public SecurityManager getSecurityManager(UserRealm userRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean//设置 shiro 过滤器:作用就是过滤拦截 鉴权当前用户是否有 对应的角色和权限
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
// 将 securityManager 和 拦截器进行绑定
shiroFilterFactoryBean.setSecurityManager(securityManager);
//没有登录跳转的页面
shiroFilterFactoryBean.setLoginUrl("/user/toLongin");
//认证成功跳转的页面
shiroFilterFactoryBean.setSuccessUrl("/user/toIndex");
//登录会权限不足跳转的页面,注解的权限或者角色不能直接跳转权限不足页面,需要手动设置
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");
Map<String, String> filterChainDefinitionMap = new HashMap<>();
filterChainDefinitionMap.put("/user/login","anon");
//配置游客路径,不登录也可以访问
filterChainDefinitionMap.put("/guest/*","anon");
//访问当前路径,当前必须拥有root角色,roles[root,manager],表示必须拥有root,manager角色
filterChainDefinitionMap.put("/user/add","roles[root]");
//访问当前路径必须拥有user:delete 权限
filterChainDefinitionMap.put("/user/delete","perms[user:delete]");
// 拦截所有路径 ***** 一定要写在再最后
filterChainDefinitionMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/* @Bean // 处理基于注解的权限 @RequiresRoles @RequiresParam @RequiresGuest 。。。
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
//将 AuthorizationAttributeSourceAdvisor 和 SecurityManager
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourceAdvisor;
}
@Bean // <aop:aspectj-autoproxy/> 开启aop 注解,像容器注入一个 DefaultAdvisorAutoProxyCreator
@ConditionalOnMissingBean //当容器中没有当前DefaultAdvisorAutoProxyCreator 实例时,注入,如果有就不注入
DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 使用 cglib 完成代理
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean// 当前异常 处理的就是 shiro基于注解的鉴权时,不满足权限需要抓住异常
SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver=new SimpleMappingExceptionResolver();
// Properties 就是读取配置文件的键值对
Properties property = new Properties();
// key 就是要捕捉 异常的权限定名
property.setProperty("org.apache.shiro.authz.AuthorizationException","/403.html");
resolver.setExceptionMappings(property);
return resolver;
}
@Bean//ShiroDialect整合shiro-thymeleaf
ShiroDialect getShiroDialect(){
ShiroDialect shiroDialect=new ShiroDialect();
return shiroDialect;
}*/
}
6.配置用户登录,创建controller
package com.aaa.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/toLongin")
public String toLongin(){
return "login";
}
@RequestMapping("/toIndex")
public String toIndex(){
return "index";
}
@RequestMapping("/login")
public String login(String username,String password){
//获取当前用户
Subject subject= SecurityUtils.getSubject();
AuthenticationToken token=new UsernamePasswordToken(username,password);
subject.login(token);
//如果登录成功
if (subject.isAuthenticated()){
return "index";
}else {
return "login";
}
}
@RequestMapping("/unauthorized")
public String unauthorized(){
return "403";
}
@RequestMapping("/add")
public String addUser(){
return "addUser";
}
@RequestMapping("/delete")
public String deleteUser(){
return "delete";
}
@RequestMapping("/logout")
public String logout(){
Subject subject=SecurityUtils.getSubject();
if (subject.isAuthenticated()){
subject.logout();
}
return "login";
}
}
7在resource文件夹下新建templates文件夹创建和controller层对应的html界面
8 测试,浏览器输入 http://localhost:8080,自动跳转到http://localhost:8080/user/toLogin
输入账户 root 密码12345