目录
1.5将Realm放入SecurityMaanager中管理
1、自定义Realm(单个Realm)
1.1编写实体类
package com.example.shrio.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName(value = "users2")
public class Users {
@TableId(value = "uid",type = IdType.AUTO)
private Integer id;
private String username;
private String password;
}
1.2编写Mapper接口
package com.example.shrio.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shrio.domain.Users;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UsersMapper extends BaseMapper<Users> {
}
1.3启动类加载Mapper接口
package com.example.shrio;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan
public class ShrioApplication {
public static void main(String[] args) {
SpringApplication.run(ShrioApplication.class, args);
}
}
1.4 自定义Realm
@component将MyRealm类实例化放入Spring容器中管理,相当于<bean id=“myRealm” class="" />
package com.example.shrio.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.shrio.domain.Users;
import com.example.shrio.mapper.UsersMapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
//自定义Realm类
@Component
public class MyRealm extends AuthorizingRealm {
@Resource
private UsersMapper usersMapper;
//自定义认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1、获取用户输入的用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 2、根据用户名查询用户
QueryWrapper<Users> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("username",token.getUsername());
Users users = usersMapper.selectOne(queryWrapper);
// 3、将查询到的用户封装成认证信息
if(users==null){
System.out.println("用户名不存在");
}
/**
* 参数一: 用户
* 参数二: 密码
* 参数三: Realm名
*/
return new SimpleAuthenticationInfo(users,users.getPassword(),"myRealm");
}
//自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
1.5将Realm放入SecurityMaanager中管理
package com.example.shrio.config;
import com.example.shrio.realm.MyRealm;
import org.apache.ibatis.annotations.Param;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ScurityConfig {
//将SecurityManager交予容器管理
@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager.setRealm(jdbcRealm);
securityManager.setRealm(myRealm);
return securityManager;
}
}
2、自定义Realm(多个Realm认证)
2.1编写实体类
package com.example.shrio.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("admin")
public class Admin {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String name;
private String password;
}
2.2创建mapper接口
package com.example.shrio.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shrio.domain.Admin;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AdminMapper extends BaseMapper<Admin> {
}
2.3自定义Realm(多Realm)
package com.example.shrio.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.shrio.domain.Users;
import com.example.shrio.mapper.UsersMapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
//自定义Realm类
@Component
public class MyRealm extends AuthorizingRealm {
@Resource
private UsersMapper usersMapper;
//自定义认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1、获取用户输入的用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 2、根据用户名查询用户
QueryWrapper<Users> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("username",token.getUsername());
Users users = usersMapper.selectOne(queryWrapper);
// 3、将查询到的用户封装成认证信息
if(users==null){
System.out.println("用户名不存在");
}
/**
* 参数一: 用户
* 参数二: 密码
* 参数三: Realm名
*/
return new SimpleAuthenticationInfo(users,users.getPassword(),"myRealm");
}
//自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
package com.example.shrio.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.shrio.domain.Admin;
import com.example.shrio.mapper.AdminMapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component(value = "myRealm2")
public class MyRealm2 extends AuthorizingRealm {
@Resource
private AdminMapper adminMapper;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
QueryWrapper<Admin> wrapper = new QueryWrapper<Admin>().eq("name", username);
Admin admin = adminMapper.selectOne(wrapper);
if(admin==null){
System.out.println("admin不存在");
}
return new SimpleAuthenticationInfo(admin,admin.getPassword(),"myRealm2");
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
2.4修改SecurityConfig配置类
//将SecurityManager交予容器管理
@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm, MyRealm2 myRealm2){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
List<Realm> list=new ArrayList<>();
list.add(myRealm);
list.add(myRealm2);
securityManager.setRealms(list);
return securityManager;
}
2.5登录认证
controller层:
@PostMapping("/login3")
public String login3(String username,String password){
return loginService.login3(username,password);
}
service层:
package com.example.shrio.service;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Service
public class LoginService {
@Resource
private DefaultWebSecurityManager securityManager;
/**
* 将Shiro交予容器管理
* @param username
* @param password
* @return
*/
public String login3(String username,String password){
// 1、将SecurityManager添加到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 2、获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 3、将前端传过来的用户名和密码封装成Shiro对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 4、shiro认证
try{
subject.login(token);
// 5、认证通过
return "main";
}catch (AuthenticationException exception){
// 6、认证失败
return "fail";
}
}
}
3、多Realm认证策略
如果有多个Realm,怎样才能认证成功,这就是认证策略。认证策略主要使用的是 AuthenticationStrategy
接口,这个接口有三个实现类
策略 | 意义 |
---|---|
AtLeastOneSuccessfulStrategy(默认) | 只要有一个Realm验证成功即可,返回所有成功的认证信息 |
FirstSuccessfulStrategy | 只要有一个Realm验证成功即可,只返回第一个成功的认证信息,其他的忽略 |
AllSuccessfulStrategy | 所有Realm验证成功才算成功,如果有一个失败则认证失败 |
// 认证策略
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator(){
// 设置认证策略
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
// 默认认证策略(一个登录成功则成功)
// authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
// 第一个成功则成功
// authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
// 全部认证成功则成功
authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
return authenticator;
}
//将SecurityManager交予容器管理
@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm, MyRealm2 myRealm2){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// securityManager.setRealm(jdbcRealm);
// securityManager.setRealm(myRealm);
securityManager.setAuthenticator(modularRealmAuthenticator());
List<Realm> list=new ArrayList<>();
list.add(myRealm);
list.add(myRealm2);
securityManager.setRealms(list);
return securityManager;
}
4、异常处理
当Shiro认证失败后,会抛出AuthorizationException
异常。该异常的子类分别代表不同的认证失败原因,我们可以通过捕捉它们确定认证失败原因。
- DisabledAccountException:账户失效
- ConcurrentAccessException:竞争次数过多
- ExcessiveAttemptsException:尝试次数过多
- UnknownAccountException:用户名不正确
- IncorrectCredentialsException:凭证(密码)不正确
- ExpiredCredentialsException:凭证过期
5、散列算法
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,适合于对密码进行加密。比如密码admin
,产生的散列值是21232f297a57a5a743894a0e4a801fc3
,但在md5解密网站很容易的通过散列值得到密码admin
。所以在加密时我们可以加一些只有系统知道的干扰数据,这些干扰数据称之为“盐”,并且可以进行多次加密,这样生成的散列值相对来说更难破解。Shiro支持的散列算法:Md2Hash、Md5Hash、Sha1Hash、Sha256Hash、Sha384Hash、Sha512Hash
package com.example.shrio;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ShiroTestMain {
@Test
public void tst1(){
/**
* 参数一 加密密码
* 参数二 盐
* 参数三 加密次数
*/
Md5Hash result=new Md5Hash("admin");
System.out.println(result);
Md5Hash result2=new Md5Hash("admin","sxt");
System.out.println(result2);
Md5Hash result3 = new Md5Hash("admin123", "sxt", 5);
System.out.println(result3);
}
}
6、加密认证
6.1数据
6.2修改自定义的Realm类
package com.example.shrio.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.shrio.domain.Admin;
import com.example.shrio.mapper.AdminMapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component(value = "myRealm2")
public class MyRealm2 extends AuthorizingRealm {
@Resource
private AdminMapper adminMapper;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
QueryWrapper<Admin> wrapper = new QueryWrapper<Admin>().eq("name", username);
Admin admin = adminMapper.selectOne(wrapper);
if(admin==null){
System.out.println("admin不存在");
}
/**
* 参数一: 用户
* 参数二: 密码
* 参数三: 盐
* 参数四: Realm名
*/
return new SimpleAuthenticationInfo(admin,
admin.getPassword(),
ByteSource.Util.bytes(admin.getSalt()),
"myRealm2");
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
6.3在注册Realm中添加加密算法
//配置加密算法
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(5);
return matcher;
}
//将SecurityManager交予容器管理
@Bean
public DefaultWebSecurityManager securityManager(MyRealm2 myRealm2){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
myRealm2.setCredentialsMatcher(hashedCredentialsMatcher());
securityManager.setRealm(myRealm2);
return securityManager;
}