【多个Realm验证单点登录SSO】shiro多个Realm验证单点登录SSO步骤 Realm重写父类方法 实现多类型登录认证授权

导读:本篇文章讲解 【多个Realm验证单点登录SSO】shiro多个Realm验证单点登录SSO步骤 Realm重写父类方法 实现多类型登录认证授权,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

这个简单的demo主要有三个微服务组成:
在这里插入图片描述
sso为单点登录的请求服务,system_service为管理员登录验证服务,member为会员登录验证服务。

大致流程就画个图给大家看
在这里插入图片描述

SSO

在这里插入图片描述

controller

/**
 * @description: ssocontroller
 * @return {@link null}
 * @author  wangxihao
 * @email wangxh0108@163.com
**/
@RestController
@RequestMapping("/member")
public class MemberLoginController {

    @Resource
    private AuthenticationService remoteMemberService;

    /**
     * 登录
     * @param userName
     * @param password
     * @return
     */
    @GetMapping("/login")
    public Result getMember(String userName,String password){
        return new Result(ResultStatus.SUCCESS.getReturncode(), ResultStatus.SUCCESS.getReturnMessage(),remoteMemberService.checkUsernameAndPassword(userName,password));
    }

    /**
     * 验证Token
     * @param token
     * @return
     */
    @GetMapping("/reLogin")
    public Result checkUser(String token){
        if(remoteMemberService.checkToken(token)){
            return new Result(ResultStatus.SUCCESS.getReturncode(), ResultStatus.SUCCESS.getReturnMessage(),"成功!");
        }
        return new Result(ResultStatus.ERROR.getReturncode(), ResultStatus.ERROR.getReturnMessage(),"失败!");
    }
}
@RestController
@RequestMapping("/user")
public class UserLoginController {
    @Resource
    private AuthenticationService remoteUserService;

    @GetMapping("/login")
    public Result getUser(String userName,String passWord){
        return new Result(ResultStatus.SUCCESS.getReturncode(), ResultStatus.SUCCESS.getReturnMessage(),remoteUserService.checkUsernameAndPassword(userName,passWord));
    }
    @GetMapping("/reLogin")
    public Result checkUser(String token){
        if(remoteUserService.checkToken(token)){
            return new Result(ResultStatus.SUCCESS.getReturncode(), ResultStatus.SUCCESS.getReturnMessage(),"成功!");
        }
        return new Result(ResultStatus.ERROR.getReturncode(), ResultStatus.ERROR.getReturnMessage(),"失败!");
    }
}

AuthenticationService 服务层

AuthenticationService 服务层 两个controller同调用这一层

public interface AuthenticationService {
    /**
     * 登录功能,校验用户名密码是否正确,正确生成token  返回并存入redis
     * @param userName
     * @param passWord
     * @return
     */
    Result checkUsernameAndPassword(String userName, String passWord);

    /**
     * 验证token
     * @param token
     * @return
     */
    boolean checkToken(String token);
}

AuthenticationService 两个实现类:

@Service("remoteMemberService")
public class RemoteMemberServiceImpl implements AuthenticationService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public Result checkUsernameAndPassword(String userName, String password) {
        //判断同户名密码是否空
        if(!StringUtils.hasText(userName)||!StringUtils.hasText(password)){
            throw new CustomException(ResultStatus.ARGUMENT_IS_NOT_NULL.getReturncode(),
                    ResultStatus.ARGUMENT_IS_NOT_NULL.getReturnMessage());
        }
        //手机用户信息
        CustomUsernamePasspordToken customUsernamePasspordToken = new CustomUsernamePasspordToken(userName, password, LoginType.MEMBER.toString());
        //获取subject
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(customUsernamePasspordToken);
            //获取用户信息
            Member member = (Member) subject.getPrincipal();
            String token = member.getUsername()+ UUID.randomUUID().toString();
            // 设置放入redis后key序列化方式,使用字符串  key是是什么放入的就是什么
            redisTemplate.setKeySerializer(RedisSerializer.string());
            //  使用Redis 字符串操作,放入对象  方便后面使用token换取用户信息
            redisTemplate.opsForValue().set(token,member);

            return new Result(ResultStatus.SUCCESS.getReturncode(),ResultStatus.SUCCESS.getReturnMessage(),token);

        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return  new Result(ResultStatus.ERROR.getReturncode(),
                ResultStatus.ERROR.getReturnMessage(),"用户名或者密码错误!");
    }

    @Override
    public boolean checkToken(String token) {
        return redisTemplate.hasKey(token);
    }
}
@Service("remoteUserService")
public class RemoteUserServiceImpl implements AuthenticationService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Result checkUsernameAndPassword(String userName, String passWord) {
        //判断同户名密码是否空
        if(!StringUtils.hasText(userName)||!StringUtils.hasText(passWord)){
            throw new CustomException(ResultStatus.ARGUMENT_IS_NOT_NULL.getReturncode(),
                    ResultStatus.ARGUMENT_IS_NOT_NULL.getReturnMessage());
        }
        //手机用户信息
        CustomUsernamePasspordToken customUsernamePasspordToken = new CustomUsernamePasspordToken(userName, passWord, LoginType.USER.toString());
        //获取subject
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(customUsernamePasspordToken);
            //获取用户信息
            User user = (User) subject.getPrincipal();
            String token = user.getUserName()+ UUID.randomUUID().toString();
            // 设置放入redis后key序列化方式,使用字符串  key是是什么放入的就是什么
            redisTemplate.setKeySerializer(RedisSerializer.string());
            //  使用Redis 字符串操作,放入对象  方便后面使用token换取用户信息
            redisTemplate.opsForValue().set(token,user);

            return new Result(ResultStatus.SUCCESS.getReturncode(),ResultStatus.SUCCESS.getReturnMessage(),token);

        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return  new Result(ResultStatus.ERROR.getReturncode(),
                ResultStatus.ERROR.getReturnMessage(),"用户名或者密码错误!");
    }

    @Override
    public boolean checkToken(String token) {
        return redisTemplate.hasKey(token);
    }
}

重写Realm的验证

这个类注意别忘了加入Bean容器中

/**
 * @description: 自定义验证realm的方法  重写源码中
 *
 * @return {@link null}
 * @author  wangxihao
 * @email wangxh0108@163.com
**/
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {



    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {

        //断言判断是否存在realm
        this.assertRealmsConfigured();
        //向下转型 authenticationToken转为自定义的
        CustomUsernamePasspordToken customUsernamePasspordToken = (CustomUsernamePasspordToken) authenticationToken;
        //得到登录类型  admin tiger LoginType.USER("user")
        String loginType = customUsernamePasspordToken.getLoginType();
        //得到真正配置的Realm并判断 (UserRealm MemberRealm)
        Collection<Realm> realms = this.getRealms();
        //定义Realm集合
        ArrayList<Realm> customRealm = new ArrayList<>();
        //遍历Realm
        for (Realm realm : realms) {
            //判断当前Realm名称中是否包含User or Member
            if(realm.getName().toLowerCase().contains(loginType.toLowerCase())){
                customRealm.add(realm);
            }
        }
        //有一个Realm的话 将当前realms.iterator().next()传入   多个的话 将  realms集合传入
        return realms.size() == 1 ? this.doSingleRealmAuthentication(realms.iterator().next(), customUsernamePasspordToken) : this.doMultiRealmAuthentication(realms, customUsernamePasspordToken);
    }
}

自定义Realm

public class MemberRealm extends AuthorizingRealm {


    @Autowired
    private RemoteMemberService remoteMemberService;


    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String userName = authenticationToken.getPrincipal().toString();
        System.out.println("用户名"+userName);
        Member realMember = remoteMemberService.queryByUsername(userName);
        if(realMember == null){
            throw new AccountException();
        }
        System.out.println(realMember);
        return new SimpleAuthenticationInfo(realMember,realMember.getPassword(),getName());
    }
}
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private RemoteUserService remoteUserService;
    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String userName = authenticationToken.getPrincipal().toString();
        System.out.println("用户名"+userName);
        User realUser = remoteUserService.queryByUserName(userName);
        if(realUser == null){
            throw new AccountException();
        }
        return new SimpleAuthenticationInfo(realUser,realUser.getPassword(),getName());
    }
}

自定义Token

/**
 * @description: 收集是用户信息还是会员信息  使用UsernamePasswordToken时重新父类的方法添加属性
 *
 * @return {@link null}
 * @author  wangxihao
 * @email wangxh0108@163.com
**/
@Data
public class CustomUsernamePasspordToken extends UsernamePasswordToken {
    //登录类型
    private String loginType;

    //构造器中调用父类构造 并添加子类构造中的属性
    public CustomUsernamePasspordToken(String username, String password, String loginType) {
        super(username, password);
        this.loginType = loginType;
    }
}

shiro配置

@Configuration
public class SpringShiroConfig {
    /**
     * 实例化ShiroFilterFactoryBean  拦截到所有的请求,根据请求的不同做不同的处理
     *                         配置处理请求的各种方式,配置SecurityManager 等等
     * @return
     */
    @Bean //<bean id=shiroFilter class = org.apache.shiro.spring.web.ShiroFilterFactoryBean>
    public ShiroFilterFactoryBean shiroFilter(){
        //实例化ShiroFilterFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //依赖注入
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        //定义一个map集合  LinkedHashMap  按照放入顺序进行获取
        Map chainDefinitionMap  = new LinkedHashMap();
        //所有功能放心  前后端分离认证拦截是基于session  每次请求的session不同
        chainDefinitionMap.put("/**","anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(chainDefinitionMap);

        return shiroFilterFactoryBean;
    }


    /**
     * 实例化shiro核心概念类 DefaultWebSecurityManager
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
        ArrayList<Realm> realms = new ArrayList<>();
        realms.add(userRealm());   //UserRealm
        realms.add(memberRealm());   //MemberRealmx1
        //依赖注入Realm
        securityManager.setRealms(realms);
        return securityManager;
    }

    /**
     * 配置realm 获取安全数据类
     * @return
     */
    @Bean
    public UserRealm userRealm(){
        UserRealm userRealm =new UserRealm();
        return userRealm;
    }
    @Bean
    public MemberRealm memberRealm(){
        MemberRealm memberRealm =new MemberRealm();
        return memberRealm;
    }

Feign远程调用(也可以直接查数据库,没必要远程调用)

@FeignClient("MemberServer")
public interface RemoteMemberService {
    /**
     * 远程调用member
     */
    @GetMapping("/member/queryByUsername")
    public Member queryByUsername(@RequestParam("userName") String userName);
}

@FeignClient("SystemServer")
public interface RemoteUserService {
    /**
     * 远程调用member
     */
    @GetMapping("/user/queryByUserName")
    public User queryByUserName(@RequestParam("userName")String userName);
}

application.yml

#端口号
server:
  port: 14860
spring:
  application:
    #服务名称   注册后的名称
    name: SsoServer
  cloud:
    nacos:
      discovery:
        #nacos注册中心地址
        server-addr:  localhost:8848
  #redis配置    RedisTemplate可以直接使用这些配置
  redis:
    #集群配置
    #cluster:
    #  nodes:  192.168.170.41:6001,192.168.170.41:6002,192.168.170.42:6003,192.168.170.42:6004,192.168.170.43:6005,192.168.170.43:6006
    #单机版配置
    host: 127.0.0.1
    port: 6379
    #连接属性配置
    database: 0
    timeout: 30000ms
    jedis:
      pool:
        max-active: 20000
        max-idle: 0
        max-wait: 20000ms
  main:
    allow-bean-definition-overriding: true
#swagger配置
swagger:
  base-package: com.aaa.sso.controller
  title: "电商项目-单点登录swagger"
  description: "描述"
  version: "3.0"
  contact:
    name: "AAA"
    email: "test@163.com"
    url: "https://www.baidu.com"
  terms-of-service-url: "服务条款:https://www.baidu.com"

其他两个服务我就不写了,就是一个远程调用查数据库。

结果:
在这里插入图片描述
通过相应的Realm验证之后又token生成并存储在数据库。
完事!
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/75446.html

(0)
小半的头像小半

相关推荐

极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!