ldap结合shiro搭建统一登录平台


一、ldap登录

ldap相关的文档请参考其他资料,再次不再详述。假设ldap已经存储了用户的相关信息数据;目前需要再其他服务中调用ldap的用户信息来登录。如下

1.控制器层

 @ApiOperation(value = "登录", notes = "", httpMethod = "GET")
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public ResponseBean findByCn(@RequestParam(required = true) String uid,
                                 @RequestParam(required = true) String userPassword) throws InvalidNameException, CetcBigDataException {
        return  new ResponseBean(ErrorCode.SUCCESS.getCode(),ErrorCode.SUCCESS.getMsg(), ldapService.login(uid,userPassword));

    }

2.ldap登录代码;@Service

public class LdapService { @Autowired private LdapTemplate ldapTemplate; @Autowired private JWTUtil jwtUtil; @Value("${spring.ldap.urls}") private String url; @Value("${spring.ldap.base}") private String basedn; @Value("${spring.ldap.domain}") private String domain; private Hashtable<String, String> env = new Hashtable<String, String>(); public ActiveUser login(String uid, String userPassword) throws CetcBigDataException { boolean loginFlag=ldapAuth(uid,userPassword); // boolean loginFlag=connect(uid,userPassword);

        if (!loginFlag){ throw new CetcBigDataException("登录失败"); } ActiveUser person=new ActiveUser(); person.setUserCode(uid); person.setToken(jwtUtil.sign(uid,userPassword)); return person; } /** * 方法一:AD认证 * * @param username 用户名 * @param password 密码 */ boolean ldapAuth(String username, String password) { EqualsFilter filter = new EqualsFilter("uid", username); return ldapTemplate.authenticate("", filter.toString(), password); }
  /**
  * 方法二,
AD认证
  */
public boolean connect(String userName,String passwd) {
 boolean result=false; LdapContext ldapContext = null; //用户名称,cn,ou,dc 分别:用户,组,域 env.put(Context.SECURITY_PRINCIPAL, "uid="+userName+","+domain+","+basedn); //用户密码 cn 的密码  env.put(Context.SECURITY_CREDENTIALS, passwd); //url 格式:协议://ip:端口/组,域 ,直接连接到域或者组上面 env.put(Context.PROVIDER_URL, url+"/"+basedn); //LDAP 工厂 env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); //验证的类型 "none", "simple", "strong" env.put(Context.SECURITY_AUTHENTICATION, "simple"); try { ldapContext = new InitialLdapContext(env, null); result=true; System.out.println("---connection is ready----"); } catch (NamingException e) { System.out.println("--- get connection failure ----"); } return result; } }

3.登录时,需要生成token。用于shiro权限验证

 /**
     * 生成签名,24小时后过期
     * @param username 用户名
     * @param secret 用户的密码
     * @return 加密的token
     */
    public  String sign(String username, String secret) {
        try {
            Date date = new Date(System.currentTimeMillis() + TOKEN_EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带username信息
            String token =  JWT.create()
                    .withClaim("username", username)
                    .withExpiresAt(date)
                    .sign(algorithm);
            redisTemplate.opsForValue().set("token::"+username,token);
            redisTemplate.expire("token:"+username,REDIS_TOKEN_EXPIRE_TIME, TimeUnit.DAYS);
            return token;
        } catch (Exception e) {
            LOG.error("签名失败 {}",username);
            return null;
        }
    }

 

二、shiro相关配置

shiro配置文件如下,将CustomRealm注入DefaultWebSecurityManager。

@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public DefaultWebSecurityManager getManager(CustomRealm realm, @Value("${spring.redis.host}") String host,
                                                @Value("${spring.redis.port}") Integer port) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // 使用自己的realm
        manager.setRealm(realm);
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        // 自定义缓存实现 使用redis
        manager.setCacheManager(cacheManager(host, port));
        // 自定义session管理 使用redis
        manager.setSessionManager(sessionManager(host, port));

        manager.setSubjectDAO(subjectDAO);
        return manager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("jwt", new JWTFilter());
        factoryBean.setFilters(filterMap);
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setUnauthorizedUrl("/401");
        /*
         * 自定义url规则 http://shiro.apache.org/web.html#urls-
         */
        Map<String, String> filterRuleMap = new HashMap<>(16);
        // 放行swagger
        filterRuleMap.put("/swagger-ui.html", "anon");
        filterRuleMap.put("/swagger-resources", "anon");
        filterRuleMap.put("/v2/api-docs", "anon");
        filterRuleMap.put("/webjars/springfox-swagger-ui/**", "anon");
        filterRuleMap.put("/wechatLogin", "anon");
        filterRuleMap.put("/wechatInfo", "anon");
        filterRuleMap.put("/get-message", "anon");
        filterRuleMap.put("/get-appMessage", "anon");
        filterRuleMap.put("/label-get-token", "anon");
        // 访问401和404页面不通过我们的Filter
        filterRuleMap.put("/401", "anon");
        filterRuleMap.put("/**", "jwt");
        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

    /**
     * Shiro生命周期处理器
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题
        // https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * cacheManager 缓存 redis实现 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisCacheManager cacheManager(String host, Integer port) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager(host, port));
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager(String host, Integer port) {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        // 配置缓存过期时间
        redisManager.setExpire(1800);
        return redisManager;
    }

    /**
     * Session Manager 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager(String host, Integer port) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO(host, port));
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO(String host, Integer port) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager(host, port));
        return redisSessionDAO;
    }
CustomRealm主要用于接口验证用户正确与否,需要和shiro相关注解搭配使用,如下realm配置好后,控制器接口加入注解@RequiresAuthentication 就会对token进行验证
*/
@Component
public class CustomRealm  extends AuthorizingRealm {
    private final Logger LOG = LoggerFactory.getLogger(CustomRealm.class) ;
    @Lazy
    @Autowired
    private LdapService ldapService;

    @Lazy
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 必须重写此方法,不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = JWTUtil.getUsername(principals.toString());
        String getPrimaryPrincipal=(String) principals.getPrimaryPrincipal();
        LOG.info(getPrimaryPrincipal);
        LOG.info(JWTUtil.getUsername(getPrimaryPrincipal));

        Person user = ldapService.findByUid(username);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        // Set<String> permission = new HashSet<>(Arrays.asList(user.getPermission().split(",")));
        // simpleAuthorizationInfo.addStringPermissions(permission);
        // redisTemplate.opsForValue().getAndSet("user",user);
        return simpleAuthorizationInfo;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth)  throws AuthenticationException {

        //前端传过来的token
        String token = (String) auth.getCredentials();
        // 解密获得username,用于和数据库进行对比
        String username = JWTUtil.getUsername(token);
        if (username == null) {
            // return null;
            LOG.warn("token invalid");
            throw new IncorrectCredentialsException();
        }
        Person userBean = ldapService.findByUid(username);
        if (userBean == null) {
            LOG.warn("User {} didn't existed!",username);
            throw new UnknownAccountException("User didn't existed!");
        }
        try {
            JWTUtil.verifyEx(token, username ,userBean.getUserPassword());
        }catch (Exception e) {
            if (e instanceof TokenExpiredException){
                Boolean tokenFlag = redisTemplate.hasKey("token:" + username);
                if (!tokenFlag){
                    throw new AuthenticationException("Authorization 过期");
                }
            }else{
                LOG.warn("Username  {} or password error",username);
                throw new AuthenticationException("token error");
            }
        }
        return new SimpleAuthenticationInfo(token, token, "my_realm");
    }

}
 @ApiOperation(value = "查询所有person", notes = "", httpMethod = "GET")
    @RequestMapping(value = "/findAll",method = RequestMethod.GET)
    @RequiresAuthentication
    public ResponseBean findAll(HttpServletRequest request) throws InvalidNameException {
        String token = request.getHeader("Authorization");
        String userCode = JWTUtil.getUsername(token);
        if (userCode == null) {
            return new ResponseBean(ErrorCode.UNAUTHORIZED.getCode(), ErrorCode.UNAUTHORIZED.getMsg(),  null);
        }

        return  new ResponseBean(ErrorCode.SUCCESS.getCode(),ErrorCode.SUCCESS.getMsg(), ldapService.getAllPersonNames());

    }

 

查询ldap用户的方法如下:

  public Person findByUid(String uid)  {

        List<Person>  personList= ldapTemplate.search((LdapQuery) query().where("uid").is(uid),new PersonAttributesMapper());
        if (CollectionUtils.isEmpty(personList)){
            return null;
        }

        Person person=personList.get(0);

        return person;
    }
PersonAttributesMapper代码如下:

public class PersonAttributesMapper  implements AttributesMapper<Person> {


    @Override
    public Person mapFromAttributes(Attributes attrs) throws NamingException {
        Person person = new Person();
        person.setSuerName((String) attrs.get("sn").get());
        person.setCommonName((String) attrs.get("cn").get());
        person.setUid((String) attrs.get("uid").get());
        byte[] bytes= (byte[]) attrs.get("userPassword").get();

        person.setUserPassword(new String(bytes));

        return person;

    }
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM