1、自定义realm的实现
首先自定义一个类继承AuthorizingRealm
public class MyRealm extends AuthorizingRealm {
private UserDAO userDAO=new UserDAO();
@Override
public String getName() {
return "MyRealm";
}
/**
*这个方法是用来进行认证的
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//第一步:通过token获取到用户数据
String userName = (String) authenticationToken.getPrincipal();
//第二步:通过用户名 去数据库查询用户对象
User user=null;
try {
user = userDAO.findUserByName(userName);
System.out.println("从数据库查询出来的数据是:"+user);
} catch (Exception e) {
System.out.println("查询失败:"+e.fillInStackTrace());
}
if(null==user){ //说明数据库里面没有查询出数据来
return null;
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),getName());
return simpleAuthenticationInfo;
}
/**
* 做用户授权的
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
编写配置文件shiro-realm.ini
[main]
customRealm=com.qf.shiro.realm.MyRealm
securityManager.realms=$customRealm
再写访问数据库的代码(此处省略)
测试成功
从数据库查询出来的数据是:User(id=1, username=xbb, password=123)
登录后用户的认证状态:true
注销后用户的认证状态false
2、MD5的使用
开发时,敏感信息在数据库存储时不能使用明文存储,shiro框架提供了密码散列的这个功能
Md5Hash md5Hash = new Md5Hash("abc");
System.out.println("散列结果:"+md5Hash);
//加salt(盐)使密码更安全 更加不容易被破解
//相当于盐值放在密码前面再进行散列
Md5Hash md5Hash1 = new Md5Hash("123","abc");
System.out.println("加盐后散列结果:"+md5Hash1);
//此处结果和上面一样
Md5Hash md5Hash2 = new Md5Hash("123abc");
System.out.println(md5Hash2);
//hashlerations:散列次数
Md5Hash md5Hash3= new Md5Hash("abc","123",2);
System.out.println("加盐加次数散列的结果:"+md5Hash3);
Md5Hash md5Hash4 = new Md5Hash("123abc");
Md5Hash md5Hash5 = new Md5Hash(md5Hash4);
System.out.println(md5Hash5);
2.1、在realm中使用散列
可以随便将数据库的密码改成加盐散列之后的结果
重新定义realm中的SimpleAuthenticationInfo方法
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user.getUsername(), //用户名
user.getPassword(), //密码
ByteSource.Util.bytes(user.getSalt()), //从数据库获取的盐值
getName()); //realm的名字
编写配置文件shiro-hash.ini
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到realm
customRealm=com.qf.shiro.md5.MyRealm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
然后就测试成功啦
3、代码授权
用户要授权必须是要在认证的基础上才能授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//第一步:获取前端传递过来的用户名
String username = (String) principalCollection.getPrimaryPrincipal();
//第二步,通过用户名从数据库查询用户所拥有的角色和对应的权限
//模拟一下查询
Set<String> roles = new HashSet<>();
roles.add("adminer");
roles.add("buyer");
roles.add("seller");
Set<String> perms = new HashSet<>();
perms.add("user:add");
perms.add("user:modify");
perms.add("user:delete");
SimpleAuthorizationInfo simpleAuthorizationInfo1 = new SimpleAuthorizationInfo(perms);
// SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
return simpleAuthorizationInfo1;
}
然后再测试一下就ok了
4、SpringBoot整合Shiro
4.1、基本的整合
导包
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</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>2.1.1</version>
</dependency>-->
<!-- <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--导入的是spring对shiro的支持包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
配置模板引擎
#配置模板引擎
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false
编写login.html和index.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
<form action="/login" method="post">
用户名: <input type="text" name="username"><span th:text="${usernameError}"></span><br>
密码: <input type="password" name="password"><span th:text="${passwordError}"></span>
<span th:text="${otherError}"></span><br>
<input type="submit" value="登录">
</form>
</body>
</html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is index page
<a href="/logout">退出</a>
</body>
</html>
实体类
public class User implements Serializable {
private static final long serialVersionUID = -6452122067767617559L;
private int id;
private String username;
private String password;
}
注意:此处实体类必须要实现序列化接口并生成serialVersionUID
然后编写shiro的配置文件
@SpringBootConfiguration
public class ShiroConfig {
private Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
//配置shiro的过滤器拦截请求
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
logger.info("shiro的过滤器执行了");
//配置认证如果失败跳转的页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
Map<String,String> map = new LinkedHashMap<>();
//第一个参数是路径,第二个参数是过滤器名字
/**
* /** 当前目录以及后面的所有子目录
* /* 这个相当于只是匹配一级目录 127.0.0.1/index
* 常见的过滤器名字
* authc:认证的过滤器
* anon:没有认证就可以访问
* map.put("/index","anon");
* logout:注销
* perms:权限控制
* roles:具有某一个角色才能访问
* 注意:/**这个配置必须是最后一个
*/
map.put("/login","anon");
maps.put("/logout","logout"); //这个就是退出功能
//所有请求必须在认证之后才能执行
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
//配置安全管理器Security Manager
@Bean
public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置校验的realm对象
logger.info("shiro的安全管理器执行了");
securityManager.setRealm(myRealm);
return securityManager;
}
//配置realm
@Bean
public MyRealm myRealm(){
logger.info("shiro的realm执行了");
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myRealm;
}
/**
* 配置密码散列
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//设置散列的方式
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//设置散列的次数
hashedCredentialsMatcher.setHashIterations(1);
return hashedCredentialsMatcher;
}
}
编写controller文件
@Controller
public class UserController {
private Logger logger = LoggerFactory.getLogger(UserController.class);
/**
* 跳转至login页面
* @return
*/
@RequestMapping("toLogin")
public String toLogin(){
return "login";
}
/**
* 跳转至index页面
* @return
*/
@RequestMapping("toIndex")
public String toIndex(){
return "index";
}
@RequestMapping("login")
public String login(User user, ModelMap map){
//封装请求对象
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
//获取登录的主体对象
Subject subject = SecurityUtils.getSubject();
//登录
try {
subject.login(token);
} catch (UnknownAccountException e){
logger.info("用户名不对");
map.put("usernameError","用户名不对");
return "login";
} catch (IncorrectCredentialsException e){
logger.info("密码不对");
map.put("passwordError","密码不对");
return "login";
} catch (Exception e){
logger.info("其他问题造成登录失败不对"+e.fillInStackTrace());
map.put("otherError","登录失败");
return "login";
}
User user1 = (User) SecurityUtils.getSubject().getPrincipal();
map.put("user",user1);
return "index";
}
}
自定义realm
public class MyRealm extends AuthorizingRealm {
@Override
public String getName() {
return "MyRealm";
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户名
String username = (String) authenticationToken.getPrincipal();
//通过用户名查询对象
if(!(username.equals("xbb"))){
return null;
}
User user = new User(1,"xbb","e99a18c428cb38d5f260853678922e03");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user,
user.getPassword(),
ByteSource.Util.bytes("abc"),
getName());
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
测试: